Files
ionic-framework/js/ext/angular/test/directive/ionicTabBar.unit.js
2014-04-01 11:07:15 -05:00

629 lines
20 KiB
JavaScript

describe('tabs', function() {
describe('miscellaneous', function() {
beforeEach(module('ionic', function($provide) {
$provide.value('$ionicViewService', {
disableRegisterByTagName: jasmine.createSpy('disableRegisterByTagName')
});
}));
it('should register tabs', inject(function($ionicViewService) {
expect($ionicViewService.disableRegisterByTagName).toHaveBeenCalledWith('ion-tabs');
}));
});
describe('ionicTabs controller', function() {
beforeEach(module('ionic'));
var ctrl, scope;
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('ionicTabs', {
$scope: scope,
$element: angular.element('<div>')
});
}));
it('.getTabIndex should return indexOf tab', function() {
expect(ctrl.selectedIndex()).toBe(-1);
var tab1 = {}, tab2 = {};
ctrl.add(tab1);
ctrl.add(tab2);
expect(ctrl.selectedIndex()).toBe(0);
ctrl.select(tab2);
expect(ctrl.selectedIndex()).toBe(1);
ctrl.deselect(tab2);
expect(ctrl.selectedIndex()).toBe(-1);
});
it('.add should add tab and select if empty, & set historyId', inject(function($ionicViewService) {
var tab1 = {};
var tab2 = {};
spyOn($ionicViewService, 'registerHistory');
spyOn(ctrl, 'select');
ctrl.add(tab1);
expect($ionicViewService.registerHistory).toHaveBeenCalledWith(tab1);
expect(ctrl.tabs).toEqual([tab1]);
expect(ctrl.select).toHaveBeenCalledWith(tab1);
ctrl.select.reset();
ctrl.add(tab2);
expect($ionicViewService.registerHistory).toHaveBeenCalledWith(tab2);
expect(ctrl.tabs).toEqual([tab1, tab2]);
expect(ctrl.select).not.toHaveBeenCalled();
}));
it('.remove should remove tab and reselect', function() {
var tab1 = {}, tab2 = {}, tab3 = {};
ctrl.add(tab1);
ctrl.add(tab2);
ctrl.add(tab3);
expect(ctrl.selectedTab()).toBe(tab1);
ctrl.select(tab3);
expect(ctrl.selectedTab()).toBe(tab3);
ctrl.remove(tab3);
expect(ctrl.selectedTab()).toBe(tab2);
expect(ctrl.tabs.indexOf(tab3)).toBe(-1);
ctrl.remove(tab1);
expect(ctrl.selectedTab()).toBe(tab2);
expect(ctrl.tabs.indexOf(tab1)).toBe(-1);
ctrl.remove(tab2)
expect(ctrl.selectedTab()).toBe(null);
expect(ctrl.tabs.indexOf(tab2)).toBe(-1);
expect(ctrl.tabs.length).toBe(0);
});
it('.deselect should unselect if visible', function() {
var tab1 = {
$tabSelected: true,
onDeselect: jasmine.createSpy('deselect'),
};
ctrl.deselect(tab1);
expect(tab1.$tabSelected).toBe(false);
expect(tab1.onDeselect).toHaveBeenCalled();
expect(ctrl.selectedTab()).toBe(null);
});
it('.deselect should do nothing if not visible', function() {
var tab1 = {
$tabSelected: false,
onDeselect: jasmine.createSpy('deselect')
};
spyOn(ctrl, 'selectedTab').andCallFake(function() {
return 'foo';
});
ctrl.deselect(tab1);
expect(tab1.$tabSelected).toBe(false);
expect(tab1.onDeselect).not.toHaveBeenCalled();
expect(ctrl.selectedTab()).toBe('foo');
});
it('.select should throw error if tab doesnt exist', function() {
var tab = {};
ctrl.add(tab);
expect(function() {
ctrl.select({});
}).toThrow();
expect(function() {
ctrl.select(null);
}).toThrow();
expect(function() {
ctrl.select(tab);
}).not.toThrow();
});
it('.select should throw error if number is bad', function() {
ctrl.add({});
expect(function() {
ctrl.select(1);
}).toThrow();
expect(function() {
ctrl.select(-1);
}).toThrow();
expect(function() {
ctrl.select(0);
}).not.toThrow();
});
it('.select should allow number', function() {
var tab1 = {}, tab2 = {};
ctrl.add(tab1);
ctrl.add(tab2);
ctrl.select(tab2);
expect(ctrl.selectedTab()).toBe(tab2);
ctrl.select(0);
expect(ctrl.selectedTab()).toBe(tab1);
ctrl.select(1);
expect(ctrl.selectedTab()).toBe(tab2);
ctrl.select(tab1);
expect(ctrl.selectedTab()).toBe(tab1);
});
it('.select on selected tab should do nothing or go to history root', inject(function($ionicViewService) {
spyOn($ionicViewService, 'goToHistoryRoot');
var tab = { $historyId: '1' };
ctrl.add(tab);
expect(ctrl.selectedTab()).toBe(tab);
//Do nothing unless emit event is passed
ctrl.select(tab);
expect($ionicViewService.goToHistoryRoot).not.toHaveBeenCalled();
ctrl.select(tab, true);
expect($ionicViewService.goToHistoryRoot).toHaveBeenCalledWith(tab.$historyId);
}));
it('.select should deselect all other tabs and set selected', function() {
var tab1 = {}, tab2 = {
onSelect: jasmine.createSpy('select')
};
ctrl.add(tab1);
ctrl.add(tab2);
spyOn(ctrl, 'deselect');
ctrl.select(tab2);
expect(ctrl.deselect).toHaveBeenCalledWith(tab1);
expect(ctrl.deselect).toHaveBeenCalledWith(tab2);
expect(tab2.$tabSelected).toBe(true);
expect(ctrl.selectedTab()).toBe(tab2);
expect(tab2.onSelect).toHaveBeenCalled();
});
it('.select with true should emit event', function() {
var tab1 = {};
var tab2 = {};
var tab3 = {
navViewName: 'viewName',
hasNavView: true,
title: 'Super Tab',
url: 'ionicframework.com',
uiSref: 'drifty'
};
var eName, eData;
spyOn(scope, '$emit').andCallFake(function(eventName, data) {
eName = eventName;
eData = data;
});
ctrl.add(tab1);
ctrl.add(tab2);
ctrl.add(tab3);
ctrl.select(tab2);
expect(scope.$emit).not.toHaveBeenCalled();
ctrl.select(tab3, true);
expect(scope.$emit).toHaveBeenCalled();
expect(eName).toBe('viewState.changeHistory');
expect(eData).toEqual({
type: 'tab',
tabIndex: 2,
historyId: tab3.$historyId,
navViewName: tab3.navViewName,
hasNavView: tab3.hasNavView,
title: tab3.title,
url: tab3.href,
uiSref: tab3.uiSref
});
});
});
describe('ionTabs directive', function() {
beforeEach(module('ionic'));
function setup(attrs, content) {
var element;
inject(function($compile, $rootScope) {
var scope = $rootScope.$new();
element = $compile('<ion-tabs ' + (attrs||'') + '>' + (content||'') + '</ion-tabs>')(scope);
scope.$apply();
});
return element;
}
it('should register with given handle and deregister on destroy', inject(function($ionicTabsDelegate) {
var deregisterSpy = jasmine.createSpy('deregister');
spyOn($ionicTabsDelegate, '_registerInstance').andCallFake(function() {
return deregisterSpy;
});
var el = setup('delegate-handle="banana"');
expect($ionicTabsDelegate._registerInstance)
.toHaveBeenCalledWith(el.controller('ionTabs'), 'banana');
expect(deregisterSpy).not.toHaveBeenCalled();
el.scope().$destroy();
expect(deregisterSpy).toHaveBeenCalled();
}));
it('should $hasTabs and $hasTabsTop', function() {
var el = setup();
var scope = el.scope();
expect(scope.$hasTabs).toBe(true);
expect(scope.$hasTabsTop).toBe(false);
el.addClass('tabs-top');
scope.$apply();
expect(scope.$hasTabs).toBe(false);
expect(scope.$hasTabsTop).toBe(true);
el.removeClass('tabs-top');
scope.$apply();
expect(scope.$hasTabs).toBe(true);
expect(scope.$hasTabsTop).toBe(false);
});
it('should transclude content with same scope', function() {
var el = setup('', '<div class="content"></div>');
expect(el[0].querySelector('.tabs .content')).toBeTruthy();
});
});
describe('ionicTab controller', function() {
beforeEach(module('ionic'));
function setup(attrs) {
var ctrl;
inject(function($controller, $rootScope) {
ctrl = $controller('ionicTab', {
$scope: $rootScope.$new(),
$attrs: attrs || {}
});
});
return ctrl;
}
it('.hrefMatchesState', inject(function($location) {
spyOn($location, 'path').andReturn('/a/b/c');
var attr = {};
var ctrl = setup(attr);
expect(ctrl.hrefMatchesState()).toBeFalsy();
attr.href = 'a';
expect(ctrl.hrefMatchesState()).toBe(false);
attr.href = '/a';
expect(ctrl.hrefMatchesState()).toBe(true);
attr.href = '#/a';
expect(ctrl.hrefMatchesState()).toBe(true);
attr.href = '#/a/b/c';
expect(ctrl.hrefMatchesState()).toBe(true);
attr.href = '#/a/b/c/';
expect(ctrl.hrefMatchesState()).toBe(true);
attr.href = '/a/b/c/';
expect(ctrl.hrefMatchesState()).toBe(true);
attr.href = '/a/b/c/d';
expect(ctrl.hrefMatchesState()).toBe(false);
attr.href = 'something';
expect(ctrl.hrefMatchesState()).toBe(false);
}));
it('.srefMatchesState', inject(function($state) {
spyOn($state, 'includes').andReturn(111);
var attr = {};
var ctrl = setup(attr);
expect(ctrl.srefMatchesState()).toBeFalsy();
expect($state.includes).not.toHaveBeenCalled();
//We won't unit test $state.includes, only that it was called
attr.uiSref = 'abc';
expect(ctrl.srefMatchesState()).toBe(111);
expect($state.includes).toHaveBeenCalledWith('abc');
$state.includes.reset();
attr.uiSref = 'def({ param: "value" })';
ctrl.srefMatchesState();
expect($state.includes).toHaveBeenCalledWith('def');
}));
it('.navNameMatchesState', inject(function($ionicViewService) {
spyOn($ionicViewService, 'isCurrentStateNavView').andReturn(123);
var ctrl = setup();
expect(ctrl.navNameMatchesState()).toBeFalsy();
ctrl.navViewName = 'foo';
expect(ctrl.navNameMatchesState()).toBe(123);
expect($ionicViewService.isCurrentStateNavView).toHaveBeenCalledWith('foo');
}));
});
describe('ionTab directive', function() {
var tabDoesMatch;
beforeEach(module('ionic', function($controllerProvider) {
$controllerProvider.register('ionicTab', function($scope) {
this.$scope = $scope;
this.tabMatchesState = jasmine.createSpy('tabMatchesState')
.andCallFake(function() {
return tabDoesMatch;
});
});
}));
var tabsCtrl, tabsEl, scope, tabEl;
function setup(attrs, content) {
inject(function($compile, $rootScope) {
tabsEl = angular.element(
'<ion-tabs>' +
'<ion-tab '+(attrs||'')+'>'+(content||'')+'</ion-tab>' +
'</ion-tabs>'
);
tabEl = tabsEl.find('ion-tab');
$compile(tabsEl)($rootScope.$new());
$rootScope.$apply();
tabsCtrl = tabsEl.data('$ionTabsController');
scope = tabsEl.scope();
spyOn(tabsCtrl, 'remove');
});
}
it('should not initially compile content until selected', inject(function($compile, $rootScope) {
var el = $compile('<ion-tabs>' +
'<ion-tab></ion-tab>' +
'<ion-tab><div ng-init="$root.elephant = \'banana\'"></div></ion-tab>' +
'</ion-tabs>')($rootScope);
$rootScope.$apply();
expect($rootScope.elephant).toBeUndefined();
el.controller('ionTabs').select(1);
$rootScope.$apply();
expect($rootScope.elephant).toBe('banana');
}));
it('should add itself to tabsCtrl and remove on $destroy', function() {
var el = setup();
var tab = tabsCtrl.tabs[0];
tab.$destroy();
expect(tabsCtrl.remove).toHaveBeenCalledWith(tab);
});
it('should compile a <ion-tab-nav> with all of the relevant attrs', function() {
setup('title=1 icon-on=2 icon-off=3 badge=4 badge-style=5 ng-click=6');
var navItem = angular.element(tabsEl[0].querySelector('.tab-item'));
//Use .scope for title because we remove title attr
//(for dom-tooltip not to appear)
expect(navItem.scope().title).toEqual('1');
expect(navItem.attr('icon-on')).toEqual('2');
expect(navItem.attr('icon-off')).toEqual('3');
expect(navItem.attr('badge')).toEqual('4');
expect(navItem.attr('badge-style')).toEqual('5');
expect(navItem.attr('ng-click')).toEqual('6');
expect(navItem.parent()[0]).toBe(tabsCtrl.$tabsElement[0]);
});
it('should remove <ion-tab-nav> on $destroy', function() {
setup();
var navItem = angular.element(tabsEl[0].querySelector('.tab-item'));
var navItemScope = navItem.isolateScope();
spyOn(navItemScope, '$destroy');
tabsCtrl.tabs[0].$destroy();
expect(navItemScope.$destroy).toHaveBeenCalled();
expect(navItem.parent().length).toBe(0);
});
it('should not set navViewName by default', function() {
setup();
expect(tabEl.controller('ionTab').navViewName).toBeUndefined();
});
angular.forEach(['ion-nav-view', 'data-ion-nav-view'], function(directive) {
it('should set navViewName if a child '+directive, inject(function($ionicViewService, $rootScope) {
setup('', '<' + directive + ' name="banana"></' + directive + '>');
spyOn(tabsCtrl, 'select');
var tab = tabsCtrl.tabs[0];
expect(tabEl.controller('ionTab').navViewName).toBe('banana');
}));
});
it('should call tabMatchesState on compile and if match select', function() {
setup();
expect(tabEl.controller('ionTab').tabMatchesState).toHaveBeenCalled();
tabDoesMatch = true;
setup();
expect(tabEl.controller('ionTab').tabMatchesState).toHaveBeenCalled();
});
it('should call selectIfMatchesState on $stateChangeSuccess', function() {
setup();
var tabMatchesState = tabEl.controller('ionTab').tabMatchesState;
tabMatchesState.reset();
spyOn(tabsCtrl, 'select');
tabDoesMatch = false;
tabEl.scope().$broadcast('$stateChangeSuccess');
expect(tabMatchesState).toHaveBeenCalled();
expect(tabsCtrl.select).not.toHaveBeenCalled();
tabMatchesState.reset();
tabDoesMatch = true;
tabEl.scope().$broadcast('$stateChangeSuccess');
expect(tabMatchesState).toHaveBeenCalled();
expect(tabsCtrl.select).toHaveBeenCalledWith(tabEl.scope());
});
it('should transclude on $tabSelected=true', function() {
setup('', '<div class="inside-content"></div>');
var tab = tabsCtrl.tabs[0];
tabsCtrl.deselect(tab);
tab.$apply();
spyOn(tab, '$broadcast');
var tabContent = tabsEl.find('.pane');
expect(tabContent.length).toBe(0);
tab.$apply('$tabSelected = true');
tabContent = tabsEl.find('.pane');
expect(tabContent.parent()[0]).toBe(tabsCtrl.$element[0]);
var contentScope = tabContent.scope();
expect(tabContent.length).toBe(1);
expect(tabContent.find('.inside-content').length).toBe(1);
spyOn(tabContent, 'remove');
spyOn(contentScope, '$destroy');
tab.$broadcast.reset();
tab.$apply('$tabSelected = false');
expect(tabContent.parent().length).toBe(0); //removed check
expect(contentScope.$destroy).toHaveBeenCalled();
});
});
describe('ionTabNav directive', function() {
beforeEach(module('ionic'));
var tabsCtrl, tabCtrl;
function setup(attrs) {
tabsCtrl = {
select: jasmine.createSpy('select'),
_selectedTab: null,
selectedTab: function() {
return this._selectedTab;
}
};
tabCtrl = {
$scope: {}
};
var element;
inject(function($compile, $rootScope) {
var scope = $rootScope.$new();
element = angular.element('<ion-tab-nav ' + (attrs||'') + '></ion-tab-nav>');
element.data('$ionTabsController', tabsCtrl);
element.data('$ionTabController', tabCtrl);
element = $compile(element)(scope);
scope.$apply();
});
return element;
}
it('should remove title attribute', function() {
var el = setup('title="something"');
expect(el[0].hasAttribute('title')).toBe(false);
expect(el.isolateScope().title).toBe('something');
});
// These next two are REALLY specific unit tests,
// but also are really really vital pieces of code
it('.isTabActive should be correct', function() {
var el = setup();
expect(el.isolateScope().isTabActive()).toBe(false);
tabsCtrl._selectedTab = tabCtrl.$scope;
expect(el.isolateScope().isTabActive()).toBe(true);
tabsCtrl._selectedTab = null;
expect(el.isolateScope().isTabActive()).toBe(false);
});
it('.selectTab should be correct and preventDefault', function() {
var el = setup();
var preventSpy = jasmine.createSpy('preventDefault');
el.isolateScope().selectTab({
preventDefault: preventSpy
});
expect(preventSpy).toHaveBeenCalled();
expect(tabsCtrl.select).toHaveBeenCalledWith(tabCtrl.$scope, true);
});
it('should fallback to icon for icon-on and icon-off', function() {
var el = setup('icon=1');
expect(el.isolateScope().getIconOn()).toBe('1');
el.isolateScope().iconOn = 2;
expect(el.isolateScope().getIconOn()).toBe(2);
el.isolateScope().iconOn = null;
expect(el.isolateScope().getIconOn()).toBe('1');
expect(el.isolateScope().getIconOff()).toBe('1');
el.isolateScope().iconOff = 3;
expect(el.isolateScope().getIconOff()).toBe(3);
el.isolateScope().iconOff = null;
expect(el.isolateScope().getIconOff()).toBe('1');
});
it('should select tab on click by default', function() {
var el = setup();
el.triggerHandler('click');
expect(tabsCtrl.select).toHaveBeenCalledWith(tabCtrl.$scope, true);
});
it('should use ngClick if defined', function() {
var el = setup('ng-click="doSomething()"');
el.scope().doSomething = jasmine.createSpy('doSomething');
el.triggerHandler('click');
expect(tabsCtrl.select).not.toHaveBeenCalled();
expect(el.scope().doSomething).toHaveBeenCalled();
});
it('should have title and only title', function() {
var el = setup('title="<b>hi, {{name}}!</b>"');
expect(el.find('.tab-title').html()).toBe('<b>hi, !</b>');
expect(el.find('.icon.on').length).toBe(0);
expect(el.find('.icon.off').length).toBe(0);
el.scope().$apply('name = "joe"');
expect(el.find('.tab-title').html()).toBe('<b>hi, joe!</b>');
});
it('should change icon class with just icon', function() {
//In this case, icon-on and icon-off should be same
var el = setup('icon={{icon}}');
el.scope().icon="superIcon";
el.scope().$apply();
el.isolateScope().isTabActive = function() { return true; };
el.isolateScope().$apply();
expect(el.find('.icon.superIcon').length).toBe(1);
el.isolateScope().isTabActive = function() { return false; };
el.isolateScope().$apply();
expect(el.find('.icon.superIcon').length).toBe(1);
});
it('should change classes based on active', function() {
var el = setup('icon-on="{{true}}" icon-off="{{false}}"');
el.isolateScope().isTabActive = function() { return true; };
el.isolateScope().$apply();
expect(el.hasClass('tab-item-active')).toBe(true);
expect(el.find('.icon.true').length).toBe(1);
expect(el.find('.icon.false').length).toBe(0);
el.isolateScope().isTabActive = function() { return false; };
el.isolateScope().$apply();
expect(el.hasClass('tab-item-active')).toBe(false);
expect(el.find('.icon.true').length).toBe(0);
expect(el.find('.icon.false').length).toBe(1);
});
it('shouldnt has-badge without badge', function() {
var el = setup();
expect(el.hasClass('has-badge')).toBe(false);
expect(el.find('.badge').length).toBe(0);
});
it('should have badge', function() {
var el = setup('badge="\'badger\'" badge-style="super-style"');
expect(el.hasClass('has-badge')).toBe(true);
expect(el.find('.badge.super-style').text()).toBe('badger');
});
});
});