angular-ui-bootstrap
Version:
Native AngularJS (Angular) directives for Bootstrap
1,059 lines (921 loc) • 36.1 kB
JavaScript
describe('tabs', function() {
var elm, scope;
beforeEach(module('ui.bootstrap.tabs'));
beforeEach(module('uib/template/tabs/tabset.html'));
beforeEach(module('uib/template/tabs/tab.html'));
function titles() {
return elm.find('ul.nav-tabs li');
}
function contents() {
return elm.find('div.tab-content div.tab-pane');
}
function expectTitles(titlesArray) {
var t = titles();
expect(t.length).toEqual(titlesArray.length);
for (var i = 0; i < t.length; i++) {
expect(t.eq(i).text().trim()).toEqual(titlesArray[i]);
}
}
function expectContents(contentsArray) {
var c = contents();
expect(c.length).toEqual(contentsArray.length);
for (var i = 0; i < c.length; i++) {
expect(c.eq(i).text().trim()).toEqual(contentsArray[i]);
}
}
describe('basics', function() {
beforeEach(inject(function($compile, $rootScope) {
scope = $rootScope.$new();
scope.first = '1';
scope.second = '2';
scope.third = '3';
scope.active = 1;
scope.firstClass = 'first-class';
scope.secondClass = 'second-class-1 second-class-2';
scope.selectFirst = jasmine.createSpy();
scope.selectSecond = jasmine.createSpy();
scope.deselectFirst = jasmine.createSpy();
scope.deselectSecond = jasmine.createSpy();
scope.deselectThird = function($event) {
$event.preventDefault();
};
elm = $compile([
'<uib-tabset class="hello" data-pizza="pepperoni" active="active">',
' <uib-tab index="1" heading="First Tab {{first}}" classes="{{firstClass}}" select="selectFirst($event)" deselect="deselectFirst($event, $selectedIndex)">',
' first content is {{first}}',
' </uib-tab>',
' <uib-tab index="2" classes="{{secondClass}}" select="selectSecond($event)" deselect="deselectSecond($event, $selectedIndex)">',
' <uib-tab-heading><b>Second</b> Tab {{second}}</uib-tab-heading>',
' second content is {{second}}',
' </uib-tab>',
' <uib-tab index="3" classes="{{thirdClass}}" deselect="deselectThird($event, $selectedIndex)">',
' <uib-tab-heading><b>Second</b> Tab {{third}}</uib-tab-heading>',
' third content is {{third}}',
' </uib-tab>',
'</uib-tabset>'
].join('\n'))(scope);
scope.$apply();
return elm;
}));
it('should pass class and other attributes on to tab template', function() {
expect(elm).toHaveClass('hello');
expect(elm.attr('data-pizza')).toBe('pepperoni');
//Ensure that we have bootstrap 4 link class so things are future proofed.
var link = $(elm.find('a')[0]);
expect(link).toHaveClass('nav-link');
});
it('should create clickable titles', function() {
var t = titles();
expect(t.length).toBe(3);
expect(t.find('> a').eq(0).text()).toBe('First Tab 1');
//It should put the uib-tab-heading element into the 'a' title
expect(t.find('> a').eq(1).children().is('uib-tab-heading')).toBe(true);
expect(t.find('> a').eq(1).children().html()).toBe('<b>Second</b> Tab 2');
});
it('should bind tabs content and set first tab active', function() {
expectContents(['first content is 1', 'second content is 2', 'third content is 3']);
expect(titles().eq(0)).toHaveClass('active');
expect(titles().eq(1)).not.toHaveClass('active');
expect(scope.active).toBe(1);
});
it('should set optional classes on each tab', function() {
expect(titles().eq(0)).toHaveClass(scope.firstClass);
var secondClassArr = scope.secondClass.split(' ');
secondClassArr.forEach(function(clazz) {
expect(titles().eq(1)).toHaveClass(clazz);
});
});
it('should change active on click', function() {
titles().eq(1).find('> a').click();
expect(contents().eq(1)).toHaveClass('active');
expect(titles().eq(0)).not.toHaveClass('active');
expect(titles().eq(1)).toHaveClass('active');
expect(scope.active).toBe(2);
});
it('should call select callback on select', function() {
expect(scope.selectFirst.calls.count()).toBe(1);
titles().eq(1).find('> a').click();
expect(scope.selectSecond).toHaveBeenCalled();
expect(scope.selectSecond.calls.argsFor(0)[0].target).toBe(titles().eq(1).find('> a')[0]);
titles().eq(0).find('> a').click();
expect(scope.selectFirst).toHaveBeenCalled();
expect(scope.selectFirst.calls.argsFor(1)[0].target).toBe(titles().eq(0).find('> a')[0]);
});
it('should call deselect callback on deselect', function() {
titles().eq(1).find('> a').click();
expect(scope.deselectFirst).toHaveBeenCalled();
expect(scope.deselectFirst.calls.argsFor(0)[0].target).toBe(titles().eq(1).find('> a')[0]);
expect(scope.deselectFirst.calls.argsFor(0)[1]).toBe(1);
titles().eq(0).find('> a').click();
expect(scope.deselectSecond).toHaveBeenCalled();
expect(scope.deselectSecond.calls.argsFor(0)[0].target).toBe(titles().eq(0).find('> a')[0]);
expect(scope.deselectSecond.calls.argsFor(0)[1]).toBe(0);
titles().eq(1).find('> a').click();
expect(scope.deselectFirst.calls.count()).toBe(2);
expect(scope.deselectFirst.calls.argsFor(1)[0].target).toBe(titles().eq(1).find('> a')[0]);
expect(scope.deselectFirst.calls.argsFor(1)[1]).toBe(1);
});
it('should prevent tab deselection when $event.preventDefault() is called', function() {
spyOn(scope, 'deselectThird');
titles().eq(2).find('> a').click();
expect(scope.active).toBe(3);
titles().eq(1).find('> a').click();
expect(scope.deselectThird).toHaveBeenCalled();
expect(scope.active).not.toBe(1);
expect(scope.active).toBe(2);
});
});
describe('basics with initial active tab', function() {
beforeEach(inject(function($compile, $rootScope) {
scope = $rootScope.$new();
function makeTab(index) {
return {
index: index,
select: jasmine.createSpy()
};
}
scope.tabs = [
makeTab(1), makeTab(3), makeTab(5), makeTab(7)
];
scope.active = 5;
elm = $compile([
'<uib-tabset active="active">',
' <uib-tab index="1" select="tabs[0].select()">',
' </uib-tab>',
' <uib-tab index="3" select="tabs[1].select()">',
' </uib-tab>',
' <uib-tab index="5" select="tabs[2].select()">',
' </uib-tab>',
' <uib-tab index="7" select="tabs[3].select()">',
' </uib-tab>',
'</uib-tabset>'
].join('\n'))(scope);
scope.$apply();
}));
function expectTabActive(activeTab) {
var _titles = titles();
angular.forEach(scope.tabs, function(tab, i) {
if (activeTab === tab) {
expect(scope.active).toBe(tab.index);
//It should only call select ONCE for each select
expect(tab.select).toHaveBeenCalled();
expect(_titles.eq(i)).toHaveClass('active');
expect(contents().eq(i)).toHaveClass('active');
} else {
expect(scope.active).not.toBe(tab.index);
expect(_titles.eq(i)).not.toHaveClass('active');
}
});
}
it('should make tab titles and set active tab active', function() {
expect(titles().length).toBe(scope.tabs.length);
expectTabActive(scope.tabs[2]);
});
});
describe('without active binding and index attributes', function() {
beforeEach(inject(function($compile, $rootScope) {
scope = $rootScope.$new();
scope.first = '1';
scope.second = '2';
elm = $compile([
'<uib-tabset>',
' <uib-tab heading="First Tab {{first}}">',
' first content is {{first}}',
' </uib-tab>',
' <uib-tab heading="Second Tab {{second}}">',
' second content is {{second}}',
' </uib-tab>',
'</uib-tabset>'
].join('\n'))(scope);
scope.$apply();
return elm;
}));
it('should bind tabs content and set first tab active', function() {
expectContents(['first content is 1', 'second content is 2']);
expect(titles().eq(0)).toHaveClass('active');
expect(titles().eq(1)).not.toHaveClass('active');
expect(elm.controller('uibTabset').active).toBe(0);
});
it('should change active on click', function() {
titles().eq(1).find('> a').click();
expect(contents().eq(1)).toHaveClass('active');
expect(titles().eq(0)).not.toHaveClass('active');
expect(titles().eq(1)).toHaveClass('active');
expect(elm.controller('uibTabset').active).toBe(1);
});
});
describe('index as strings', function() {
beforeEach(inject(function($compile, $rootScope) {
scope = $rootScope.$new();
scope.first = 'one';
scope.second = 'two';
scope.active = 'two';
elm = $compile([
'<uib-tabset active="active">',
' <uib-tab index="first" heading="First Tab">',
' first content',
' </uib-tab>',
' <uib-tab index="second" heading="Second Tab">',
' second content',
' </uib-tab>',
'</uib-tabset>'
].join('\n'))(scope);
scope.$apply();
return elm;
}));
it('should set second tab active', function() {
expect(titles().eq(0)).not.toHaveClass('active');
expect(titles().eq(1)).toHaveClass('active');
expect(elm.controller('uibTabset').active).toBe('two');
});
it('should change active on click', function() {
expect(titles().eq(0)).not.toHaveClass('active');
titles().eq(0).find('> a').click();
expect(titles().eq(0)).toHaveClass('active');
expect(titles().eq(1)).not.toHaveClass('active');
expect(elm.controller('uibTabset').active).toBe('one');
});
});
describe('tab callback order', function() {
var execOrder;
beforeEach(inject(function($compile, $rootScope) {
scope = $rootScope.$new();
execOrder = [];
scope.execute = function(id) {
execOrder.push(id);
};
elm = $compile([
'<div>',
' <uib-tabset class="hello" data-pizza="pepperoni" active="active">',
' <uib-tab index="1" heading="First Tab" select="execute(\'select1\')" deselect="execute(\'deselect1\')"></uib-tab>',
' <uib-tab index="2" select="execute(\'select2\')" deselect="execute(\'deselect2\')"></uib-tab>',
' </uib-tabset>',
'</div>'
].join('\n'))(scope);
scope.$apply();
return elm;
}));
it('should call select for the first tab', function() {
expect(execOrder).toEqual([ 'select1' ]);
});
it('should call deselect, then select', function() {
execOrder = [];
// Select second tab
titles().eq(1).find('> a').click();
expect(execOrder).toEqual([ 'deselect1', 'select2' ]);
execOrder = [];
// Select again first tab
titles().eq(0).find('> a').click();
expect(execOrder).toEqual([ 'deselect2', 'select1' ]);
});
});
describe('custom template', function() {
var $compile, $templateCache;
beforeEach(inject(function($rootScope, _$compile_, _$templateCache_) {
scope = $rootScope;
$compile = _$compile_;
$templateCache = _$templateCache_;
}));
it('should support custom templates', function() {
$templateCache.put('foo/bar.html', '<div>baz</div>');
elm = $compile('<uib-tabset template-url="foo/bar.html"></uib-tabset>')(scope);
scope.$digest();
expect(elm.html()).toBe('baz');
});
});
describe('uib-tab', function() {
var $compile, $templateCache;
beforeEach(inject(function($rootScope, _$compile_, _$templateCache_) {
scope = $rootScope;
$compile = _$compile_;
$templateCache = _$templateCache_;
}));
it('should expose the controller on the view', function() {
$templateCache.put('uib/template/tabs/tab.html', '<li class="uib-tab">{{tab.text}}</li>');
elm = $compile('<uib-tabset><uib-tab heading="Tab"></uib-tab></uib-tabset>')(scope);
scope.$digest();
var tab = titles().eq(0);
var ctrl = tab.controller('uibTab');
expect(ctrl).toBeDefined();
ctrl.text = 'foo';
scope.$digest();
expect(tab.text().trim()).toBe('foo');
});
it('should support custom templates', function() {
$templateCache.put('foo/bar.html', '<li>baz</li>');
elm = $compile('<uib-tabset><uib-tab template-url="foo/bar.html"></uib-tab></uib-tabset>')(scope);
scope.$digest();
var tabTitle = titles().eq(0);
expect(tabTitle.html()).toBe('baz');
});
});
describe('ng-repeat', function() {
var $compile, $rootScope;
beforeEach(inject(function(_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
scope = $rootScope.$new();
scope.tabs = [
makeTab(1), makeTab(3), makeTab(5), makeTab(7)
];
scope.active = 5;
elm = $compile([
'<uib-tabset active="active">',
' <uib-tab index="t.index" ng-repeat="t in tabs" active="t.active" select="t.select()">',
' <uib-tab-heading><b>heading</b> {{index}}</uib-tab-heading>',
' content {{$index}}',
' </uib-tab>',
'</uib-tabset>'
].join('\n'))(scope);
scope.$apply();
}));
function makeTab(index) {
return {
index: index,
select: jasmine.createSpy()
};
}
function titles() {
return elm.find('ul.nav-tabs li');
}
function contents() {
return elm.find('div.tab-content div.tab-pane');
}
function expectTabActive(activeTab) {
var _titles = titles();
angular.forEach(scope.tabs, function(tab, i) {
if (activeTab === tab) {
expect(scope.active).toBe(tab.index);
//It should only call select ONCE for each select
expect(tab.select).toHaveBeenCalled();
expect(_titles.eq(i)).toHaveClass('active');
expect(contents().eq(i).text().trim()).toBe('content ' + i);
expect(contents().eq(i)).toHaveClass('active');
} else {
expect(scope.active).not.toBe(tab.index);
expect(_titles.eq(i)).not.toHaveClass('active');
}
});
}
it('should make tab titles and set active tab active', function() {
expect(titles().length).toBe(scope.tabs.length);
expectTabActive(scope.tabs[2]);
});
it('should switch active when clicking', function() {
titles().eq(3).find('> a').click();
expectTabActive(scope.tabs[3]);
});
it('should switch active when changing active index', function() {
scope.$apply('active = 5');
expectTabActive(scope.tabs[2]);
});
it('should deselect all when no tabs are active', function() {
scope.active = 101;
scope.$apply();
expectTabActive(null);
expect(contents().filter('.active').length).toBe(0);
scope.active = 5;
scope.$apply();
expectTabActive(scope.tabs[2]);
});
it('should not select twice', function() {
elm.remove();
elm = null;
scope = $rootScope.$new();
scope.tabs = [
makeTab(2), makeTab(3), makeTab(5), makeTab(8)
];
scope.active = 13;
scope.select = jasmine.createSpy();
elm = $compile([
'<uib-tabset active="active">',
' <uib-tab index="t.index" ng-repeat="t in tabs" select="t.select()">',
' <uib-tab-heading><b>heading</b> {{index}}</uib-tab-heading>',
' content {{$index}}',
' </uib-tab>',
' <uib-tab index="13" select="select()">',
' <uib-tab-heading><b>heading</b> foo</uib-tab-heading>',
' content foo',
' </uib-tab>',
'</uib-tabset>'
].join('\n'))(scope);
scope.$apply();
expect(scope.select.calls.count()).toBe(1);
});
});
describe('advanced uib-tab-heading element', function() {
beforeEach(inject(function($compile, $rootScope, $sce) {
scope = $rootScope.$new();
scope.myHtml = $sce.trustAsHtml('<b>hello</b>, there!');
scope.value = true;
elm = $compile([
'<uib-tabset active="active">',
' <uib-tab index="0">',
' <uib-tab-heading ng-bind-html="myHtml" ng-show="value"></uib-tab-heading>',
' </uib-tab>',
' <uib-tab index="1"><data-uib-tab-heading>1</data-uib-tab-heading></uib-tab>',
' <uib-tab index="2"><div data-uib-tab-heading>2</div></uib-tab>',
' <uib-tab index="3"><div uib-tab-heading>3</div></uib-tab>',
'</uib-tabset>'
].join('\n'))(scope);
scope.$apply();
}));
function heading() {
return elm.find('ul li > a').children();
}
it('should create a heading bound to myHtml', function() {
expect(heading().eq(0).html()).toBe('<b>hello</b>, there!');
});
it('should hide and show the heading depending on value', function() {
expect(heading().eq(0)).not.toBeHidden();
scope.$apply('value = false');
expect(heading().eq(0)).toBeHidden();
scope.$apply('value = true');
expect(heading().eq(0)).not.toBeHidden();
});
it('should have a uib-tab-heading no matter what syntax was used', function() {
expect(heading().eq(1).text()).toBe('1');
expect(heading().eq(2).text()).toBe('2');
expect(heading().eq(3).text()).toBe('3');
});
});
//Tests that http://git.io/lG6I9Q is fixed
describe('tab ordering', function() {
beforeEach(inject(function($compile, $rootScope) {
scope = $rootScope.$new();
scope.tabs = [
{ title:'Title 1', available:true },
{ title:'Title 2', available:true },
{ title:'Title 3', available:true }
];
elm = $compile([
'<uib-tabset active="active">',
' <!-- a comment -->',
' <div>div that makes troubles</div>',
' <uib-tab index="0" heading="first">First Static</uib-tab>',
' <div>another div that may do evil</div>',
' <uib-tab index="$index + 1" ng-repeat="tab in tabs | filter:tabIsAvailable" active="tab.active" heading="{{tab.title}}">some content</uib-tab>',
' <!-- another comment -->',
' <uib-tab heading="mid">Mid Static</uib-tab>',
' a text node',
' <!-- another comment -->',
' <span>yet another span that may do evil</span>',
' <uib-tab index="$index + 4" ng-repeat="tab in tabs | filter:tabIsAvailable" heading="Second {{tab.title}}">some content</uib-tab>',
' a text node',
' <span>yet another span that may do evil</span>',
' <!-- another comment -->',
' <uib-tab index="7" heading="last">Last Static</uib-tab>',
' a text node',
' <span>yet another span that may do evil</span>',
' <!-- another comment -->',
'</uib-tabset>'
].join('\n'))(scope);
scope.tabIsAvailable = function(tab) {
return tab.available;
};
}));
it('should preserve correct ordering', function() {
function titles() {
return elm.find('ul.nav-tabs li > a');
}
scope.$apply();
expect(titles().length).toBe(9);
scope.$apply('tabs[1].available=false');
scope.$digest();
expect(titles().length).toBe(7);
scope.$apply('tabs[0].available=false');
scope.$digest();
expect(titles().length).toBe(5);
scope.$apply('tabs[2].available=false');
scope.$digest();
expect(titles().length).toBe(3);
scope.$apply('tabs[0].available=true');
scope.$digest();
expect(titles().length).toBe(5);
scope.$apply('tabs[1].available=true');
scope.$apply('tabs[2].available=true');
scope.$digest();
expect(titles().length).toBe(9);
expect(titles().eq(0).text().trim()).toBe('first');
expect(titles().eq(1).text().trim()).toBe('Title 1');
expect(titles().eq(2).text().trim()).toBe('Title 2');
expect(titles().eq(3).text().trim()).toBe('Title 3');
expect(titles().eq(4).text().trim()).toBe('mid');
expect(titles().eq(5).text().trim()).toBe('Second Title 1');
expect(titles().eq(6).text().trim()).toBe('Second Title 2');
expect(titles().eq(7).text().trim()).toBe('Second Title 3');
expect(titles().eq(8).text().trim()).toBe('last');
});
});
describe('uib-tabset controller', function() {
function mockTab(index) {
return {
index: index,
onSelect : angular.noop,
onDeselect : angular.noop
};
}
var ctrl;
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope;
//instantiate the controller stand-alone, without the directive
ctrl = $controller('UibTabsetController', {$scope: scope});
}));
describe('select', function() {
it('should mark given tab selected', function() {
ctrl.tabs = [
{
tab: mockTab(0),
index: 0
}
];
ctrl.select(0);
expect(ctrl.active).toBe(0);
});
it('should deselect other tabs', function() {
var tab1 = mockTab(1), tab2 = mockTab(2), tab3 = mockTab(3);
ctrl.addTab(tab1);
ctrl.addTab(tab2);
ctrl.addTab(tab3);
ctrl.select(0);
expect(ctrl.active).toBe(1);
ctrl.select(1);
expect(ctrl.active).toBe(2);
ctrl.select(2);
expect(ctrl.active).toBe(3);
});
});
describe('addTab', function() {
it('should append tab', function() {
var tab1 = mockTab(1), tab2 = mockTab(2);
expect(ctrl.tabs).toEqual([]);
ctrl.addTab(tab1);
expect(ctrl.tabs).toEqual([
{
tab: tab1,
index: 1
}
]);
ctrl.addTab(tab2);
expect(ctrl.tabs).toEqual([
{
tab: tab1,
index: 1
},
{
tab: tab2,
index: 2
}
]);
});
it('should select the first one', function() {
var tab1 = mockTab(1), tab2 = mockTab(2);
ctrl.addTab(tab1);
expect(ctrl.active).toBe(1);
ctrl.addTab(tab2);
expect(ctrl.active).toBe(1);
});
it('should not select first active === false tab as selected', function() {
var tab = mockTab(0);
ctrl.active = 1;
ctrl.addTab(tab);
expect(ctrl.active).toBe(1);
});
it('should retain active state when adding tab of different index', function() {
var tab1 = mockTab(1), tab2 = mockTab(2);
ctrl.active = 2;
ctrl.addTab(tab1);
expect(ctrl.active).toBe(2);
ctrl.addTab(tab2);
expect(ctrl.active).toBe(2);
});
});
});
describe('remove', function() {
it('should remove title tabs when elements are destroyed and change selection', inject(function($controller, $compile, $rootScope) {
scope = $rootScope.$new();
elm = $compile('<uib-tabset active="active"><uib-tab index="0" heading="1">Hello</uib-tab><uib-tab index="$index + 1" ng-repeat="i in list" heading="tab {{i}}">content {{i}}</uib-tab></uib-tabset>')(scope);
scope.$apply();
expectTitles(['1']);
expectContents(['Hello']);
scope.$apply('list = [1,2,3]');
expectTitles(['1', 'tab 1', 'tab 2', 'tab 3']);
expectContents(['Hello', 'content 1', 'content 2', 'content 3']);
// Select last tab
titles().find('> a').eq(3).click();
expect(contents().eq(3)).toHaveClass('active');
expect(titles().eq(3)).toHaveClass('active');
// Remove last tab
scope.$apply('list = [1,2]');
expectTitles(['1', 'tab 1', 'tab 2']);
expectContents(['Hello', 'content 1', 'content 2']);
// "tab 2" is now selected
expect(titles().eq(2)).toHaveClass('active');
expect(contents().eq(2)).toHaveClass('active');
// Select 2nd tab ("tab 1")
titles().find('> a').eq(1).click();
expect(titles().eq(1)).toHaveClass('active');
expect(contents().eq(1)).toHaveClass('active');
// Remove 2nd tab
scope.$apply('list = [2]');
expectTitles(['1', 'tab 2']);
expectContents(['Hello', 'content 2']);
// New 2nd tab is now selected
expect(titles().eq(1)).toHaveClass('active');
expect(contents().eq(1)).toHaveClass('active');
}));
it('should use updated index in tab', inject(function($controller, $compile, $rootScope) {
scope = $rootScope.$new();
elm = $compile('<uib-tabset active="active"><uib-tab index="0" heading="1">Hello</uib-tab><uib-tab index="$index + 1" ng-repeat="i in list" heading="tab {{i}}">content {{i}}</uib-tab></uib-tabset>')(scope);
scope.$apply();
scope.$apply('list = [1,2,3]');
expectTitles(['1', 'tab 1', 'tab 2', 'tab 3']);
expectContents(['Hello', 'content 1', 'content 2', 'content 3']);
// Remove middle "tab 2" tab
scope.$apply('list = [1,3]');
expectTitles(['1', 'tab 1', 'tab 3']);
expectContents(['Hello', 'content 1', 'content 3']);
// Remove last "tab 3" tab
scope.$apply('list = [1]');
expectTitles(['1', 'tab 1']);
expectContents(['Hello', 'content 1']);
// Select first tab
titles().find('> a').eq(0).click();
expect(titles().eq(0)).toHaveClass('active');
expect(contents().eq(0)).toHaveClass('active');
}));
it('should not select tabs when being destroyed', inject(function($controller, $compile, $rootScope) {
var selectList = [],
deselectList = [],
getTab = function(index) {
return {
index: index,
select: function() {
selectList.push('select');
},
deselect: function() {
deselectList.push('deselect');
}
};
};
scope = $rootScope.$new();
scope.tabs = [
getTab(0),
getTab(1)
];
scope.active = 1;
elm = $compile([
'<uib-tabset active="active">',
' <uib-tab index="$index" ng-repeat="t in tabs" active="t.active" select="t.select()" deselect="t.deselect()">',
' <uib-tab-heading><b>heading</b> {{index}}</uib-tab-heading>',
' content {{$index}}',
' </uib-tab>',
'</uib-tabset>'
].join('\n'))(scope);
scope.$apply();
// The first tab is selected the during the initial $digest.
expect(selectList.length).toEqual(1);
// Destroy the tabs - we should not trigger selection/deselection any more.
scope.$destroy();
expect(selectList.length).toEqual(1);
expect(deselectList.length).toEqual(0);
}));
});
describe('disable', function() {
beforeEach(inject(function($compile, $rootScope) {
scope = $rootScope.$new();
function makeTab(disable, index) {
return {
index: index,
select: jasmine.createSpy(),
disable: disable
};
}
scope.tabs = [
makeTab(false, 0), makeTab(true, 1), makeTab(false, 2), makeTab(true, 3)
];
scope.active = 1;
elm = $compile([
'<uib-tabset active="active">',
' <uib-tab index="$index" ng-repeat="t in tabs" select="t.select()" disable="t.disable">',
' <uib-tab-heading><b>heading</b> {{index}}</uib-tab-heading>',
' content {{$index}}',
' </uib-tab>',
'</uib-tabset>'
].join('\n'))(scope);
scope.$apply();
}));
function expectTabActive(activeTab) {
var _titles = titles();
angular.forEach(scope.tabs, function(tab, i) {
if (activeTab === tab) {
expect(scope.active).toBe(tab.index);
expect(tab.select.calls.count()).toBe(tab.disable ? 0 : 1);
expect(_titles.eq(i)).toHaveClass('active');
expect(contents().eq(i).text().trim()).toBe('content ' + i);
expect(contents().eq(i)).toHaveClass('active');
} else {
expect(scope.active).not.toBe(tab.index);
expect(_titles.eq(i)).not.toHaveClass('active');
}
});
}
it('should not switch active when clicking on title', function() {
titles().eq(2).find('> a').click();
expectTabActive(scope.tabs[2]);
titles().eq(3).find('> a').click();
expectTabActive(scope.tabs[2]);
});
it('should toggle between states', function() {
expect(titles().eq(3)).toHaveClass('disabled');
scope.$apply('tabs[3].disable = false');
expect(titles().eq(3)).not.toHaveClass('disabled');
expect(titles().eq(2)).not.toHaveClass('disabled');
scope.$apply('tabs[2].disable = true');
expect(titles().eq(2)).toHaveClass('disabled');
});
});
describe('vertical', function() {
beforeEach(inject(function($compile, $rootScope) {
scope = $rootScope.$new();
scope.vertical = true;
elm = $compile('<uib-tabset vertical="vertical"></uib-tabset>')(scope);
scope.$apply();
}));
it('to stack tabs', function() {
expect(elm.find('ul.nav-tabs')).toHaveClass('nav-stacked');
});
});
describe('justified', function() {
beforeEach(inject(function($compile, $rootScope) {
scope = $rootScope.$new();
scope.justified = true;
elm = $compile('<uib-tabset justified="justified"></uib-tabset>')(scope);
scope.$apply();
}));
it('to justify tabs', function() {
expect(elm.find('ul.nav-tabs')).toHaveClass('nav-justified');
});
});
describe('type', function() {
beforeEach(inject(function($compile, $rootScope) {
scope = $rootScope.$new();
scope.navType = 'pills';
elm = $compile('<uib-tabset type="{{navType}}"></uib-tabset>')(scope);
scope.$apply();
}));
it('to show pills', function() {
expect(elm.find('ul')).toHaveClass('nav-pills');
expect(elm.find('ul')).not.toHaveClass('nav-tabs');
});
});
//https://github.com/angular-ui/bootstrap/issues/524
describe('child compilation', function() {
var elm;
beforeEach(inject(function($compile, $rootScope) {
elm = $compile('<uib-tabset><uib-tab><div></div></uib-tab></uib-tabset></div>')($rootScope.$new());
$rootScope.$apply();
}));
it('should hookup the tab\'s children to the tab with $compile', function() {
var tabChild = $('.tab-pane', elm).children().first();
expect(tabChild.inheritedData('$uibTabsetController')).toBeTruthy();
});
});
//https://github.com/angular-ui/bootstrap/issues/631
describe('ng-options in content', function() {
var elm;
it('should render correct amount of options', inject(function($compile, $rootScope) {
var scope = $rootScope.$new();
elm = $compile('<uib-tabset><uib-tab><select ng-model="foo" ng-options="i for i in [1,2,3]"></uib-tab></uib-tabset>')(scope);
scope.$apply();
var select = elm.find('select');
scope.$apply();
expect(select.children().length).toBe(4);
}));
});
//https://github.com/angular-ui/bootstrap/issues/599
describe('ng-repeat in content', function() {
var elm;
it('should render ng-repeat', inject(function($compile, $rootScope) {
var scope = $rootScope.$new();
scope.tabs = [
{title:'a', array:[1,2,3]},
{title:'b', array:[2,3,4]},
{title:'c', array:[3,4,5]}
];
elm = $compile('<div><uib-tabset>' +
'<uib-tab ng-repeat="tab in tabs" heading="{{tab.title}}">' +
'<uib-tab-heading>{{$index}}</uib-tab-heading>' +
'<span ng-repeat="a in tab.array">{{a}},</span>' +
'</uib-tab>' +
'</uib-tabset></div>')(scope);
scope.$apply();
var contents = elm.find('.tab-pane');
expect(contents.eq(0).text().trim()).toEqual('1,2,3,');
expect(contents.eq(1).text().trim()).toEqual('2,3,4,');
expect(contents.eq(2).text().trim()).toEqual('3,4,5,');
}));
});
//https://github.com/angular-ui/bootstrap/issues/783
describe('nested tabs', function() {
var elm;
it('should render without errors', inject(function($compile, $rootScope) {
var scope = $rootScope.$new();
elm = $compile([
'<div>',
' <uib-tabset class="tabbable">',
' <uib-tab heading="Tab 1">',
' <uib-tabset class="tabbable">',
' <uib-tab heading="Tab 1A">',
' </uib-tab>',
' </uib-tabset>',
' </uib-tab>',
' <uib-tab heading="Tab 2">',
' <uib-tabset class="tabbable">',
' <uib-tab heading="Tab 2A">',
' </uib-tab>',
' </uib-tabset>',
' </uib-tab>',
' </uib-tabset>',
'</div>'
].join('\n'))(scope);
scope.$apply();
// 1 outside tabset, 2 nested tabsets
expect(elm.find('.tabbable').length).toEqual(3);
}));
it('should render with the correct scopes', inject(function($compile, $rootScope) {
var scope = $rootScope.$new();
scope.tab1Text = 'abc';
scope.tab1aText = '123';
scope.tab1aHead = '123';
scope.tab2aaText = '456';
elm = $compile([
'<div>',
' <uib-tabset class="tabbable">',
' <uib-tab heading="Tab 1">',
' <uib-tabset class="tabbable">',
' <uib-tab heading="{{ tab1aHead }}">',
' {{ tab1aText }}',
' </uib-tab>',
' </uib-tabset>',
' <span class="tab-1">{{ tab1Text }}</span>',
' </uib-tab>',
' <uib-tab heading="Tab 2">',
' <uib-tabset class="tabbable">',
' <uib-tab heading="Tab 2A">',
' <uib-tabset class="tabbable">',
' <uib-tab heading="Tab 2AA">',
' <span class="tab-2aa">{{ tab2aaText }}</span>',
' </uib-tab>',
' </uib-tabset>',
' </uib-tab>',
' </uib-tabset>',
' </uib-tab>',
' </uib-tabset>',
'</div>'
].join('\n'))(scope);
scope.$apply();
var outsideTabset = elm.find('.tabbable').eq(0);
var nestedTabset = outsideTabset.find('.tabbable');
expect(elm.find('.tabbable').length).toEqual(4);
expect(outsideTabset.find('.tab-pane').eq(0).find('.tab-1').text().trim()).toEqual(scope.tab1Text);
expect(nestedTabset.find('.tab-pane').eq(0).text().trim()).toEqual(scope.tab1aText);
expect(nestedTabset.find('ul.nav-tabs li').eq(0).text().trim()).toEqual(scope.tab1aHead);
expect(nestedTabset.eq(2).find('.tab-pane').eq(0).find('.tab-2aa').text().trim()).toEqual(scope.tab2aaText);
}));
it('ng-repeat works with nested tabs', inject(function($compile, $rootScope) {
var scope = $rootScope.$new();
scope.tabs = [
{
tabs: [
{
content: 'tab1a'
},
{
content: 'tab2a'
}
],
content: 'tab1'
}
];
elm = $compile([
'<div>',
' <uib-tabset>',
' <uib-tab ng-repeat="tab in tabs">',
' <uib-tabset>',
' <uib-tab ng-repeat="innerTab in tab.tabs">',
' <span class="inner-tab-content">{{ innerTab.content }}</span>',
' </uib-tab>',
' </uib-tabset>',
' <span class="outer-tab-content">{{ tab.content }}</span>',
' </uib-tab>',
' </uib-tabset>',
'</div>'
].join('\n'))(scope);
scope.$apply();
expect(elm.find('.inner-tab-content').eq(0).text().trim()).toEqual(scope.tabs[0].tabs[0].content);
expect(elm.find('.inner-tab-content').eq(1).text().trim()).toEqual(scope.tabs[0].tabs[1].content);
expect(elm.find('.outer-tab-content').eq(0).text().trim()).toEqual(scope.tabs[0].content);
}));
});
});