describe('ng-table', function() { var dataset = [ { id: 1, name: "Moroni", age: 50, money: -10 }, { id: 2, name: "Tiancum", age: 43, money: 120 }, { id: 3, name: "Jacob", age: 27, money: 5.5 }, { id: 4, name: "Nephi", age: 29, money: -54 }, { id: 5, name: "Enos", age: 34, money: 110 }, { id: 6, name: "Tiancum", age: 43, money: 1000 }, { id: 7, name: "Jacob", age: 27, money: -201 }, { id: 8, name: "Nephi", age: 29, money: 100 }, { id: 9, name: "Enos", age: 34, money: -52.5 }, { id: 10, name: "Tiancum", age: 43, money: 52.1 }, { id: 11, name: "Jacob", age: 27, money: 110 }, { id: 12, name: "Nephi", age: 29, money: -55 }, { id: 13, name: "Enos", age: 34, money: 551 }, { id: 14, name: "Tiancum", age: 43, money: -1410 }, { id: 15, name: "Jacob", age: 27, money: 410 }, { id: 16, name: "Nephi", age: 29, money: 100 }, { id: 17, name: "Enos", age: 34, money: -100 } ]; var NgTableParams; beforeEach(module('ngTable')); var scope; beforeEach(inject(function($rootScope, _NgTableParams_) { scope = $rootScope.$new(true); NgTableParams = _NgTableParams_; })); function createNgTableParams(settings) { var initialParams; if (arguments.length === 2){ initialParams = arguments[0]; settings = arguments[1]; } settings = angular.extend({}, settings); settings.filterOptions = angular.extend({}, { filterDelay: 0 }, settings.filterOptions); var tableParams = new NgTableParams(initialParams, settings); spyOn(tableParams.settings(), 'getData').and.callThrough(); return tableParams; } describe('basics', function(){ var elm; beforeEach(inject(function($compile, $q) { elm = angular.element( '
' + '' + '' + '' + '' + '' + '' + '
' + '{{user.name}}' + '' + '{{user.age}}' + '' + '{{user.money}}' + '
' + '
'); scope.nameTitle = function(){ return 'Name of person'; }; scope.ageTitle = function(){ return 'Age'; }; scope.moneyTitle = function(){ return 'Money'; }; scope.showName = true; scope.showAge = true; scope.showMoney = true; scope.ageTitle = function(){ return 'Age'; }; scope.moneyTitle = function(){ return 'Money'; }; scope.getCustomClass = function($column){ if ($column.title().indexOf('Money') !== -1){ return 'moneyHeaderClass'; } else{ return 'customClass'; } }; scope.money = function(/*$column*/) { var def = $q.defer(); def.resolve([{ 'id': 10, 'title': '10' }]); return def; }; $compile(elm)(scope); scope.$digest(); })); it('should create table header', function() { var thead = elm.find('thead'); expect(thead.length).toBe(1); var rows = thead.find('tr'); expect(rows.length).toBe(2); var titles = angular.element(rows[0]).find('th'); expect(titles.length).toBe(3); expect(angular.element(titles[0]).text().trim()).toBe('Name of person'); expect(angular.element(titles[1]).text().trim()).toBe('Age'); expect(angular.element(titles[2]).text().trim()).toBe('Money'); expect(angular.element(rows[1]).hasClass('ng-table-filters')).toBeTruthy(); var filters = angular.element(rows[1]).find('th'); expect(filters.length).toBe(3); expect(angular.element(filters[0]).hasClass('filter')).toBeTruthy(); expect(angular.element(filters[1]).hasClass('filter')).toBeTruthy(); expect(angular.element(filters[2]).hasClass('filter')).toBeTruthy(); }); it('should create table header classes', function() { var thead = elm.find('thead'); var rows = thead.find('tr'); var titles = angular.element(rows[0]).find('th'); expect(angular.element(titles[0]).hasClass('header')).toBe(true); expect(angular.element(titles[1]).hasClass('header')).toBe(true); expect(angular.element(titles[2]).hasClass('header')).toBe(true); expect(angular.element(titles[0]).hasClass('sortable')).toBe(true); expect(angular.element(titles[1]).hasClass('sortable')).toBe(true); expect(angular.element(titles[2]).hasClass('sortable')).toBe(false); expect(angular.element(titles[0]).hasClass('customClass')).toBe(true); expect(angular.element(titles[1]).hasClass('customClass')).toBe(true); expect(angular.element(titles[2]).hasClass('moneyHeaderClass')).toBe(true); var filterCells = angular.element(rows[1]).find('th'); expect(angular.element(filterCells[0]).hasClass('filter')).toBe(true); expect(angular.element(filterCells[1]).hasClass('filter')).toBe(true); expect(angular.element(filterCells[2]).hasClass('filter')).toBe(true); expect(angular.element(filterCells[0]).hasClass('customClass')).toBe(true); expect(angular.element(filterCells[1]).hasClass('customClass')).toBe(true); expect(angular.element(filterCells[2]).hasClass('moneyHeaderClass')).toBe(true); }); it('should create table header titles', function() { var thead = elm.find('thead'); var rows = thead.find('tr'); var titles = angular.element(rows[0]).find('th'); expect(angular.element(titles[0]).attr('title').trim()).toBe('Sort by Name'); expect(angular.element(titles[1]).attr('title').trim()).toBe('Sort by Age'); expect(angular.element(titles[2]).attr('title').trim()).toBe('Sort by Money'); }); it('should show scope data', function() { var tbody = elm.find('tbody'); expect(tbody.length).toBe(1); var rows = tbody.find('tr'); expect(rows.length).toBe(0); var params = new NgTableParams({ count: 10 // count per page }, { dataset: dataset }); scope.tableParams = params; scope.$digest(); rows = tbody.find('tr'); expect(rows.length).toBe(10); scope.tableParams.page(2); scope.$digest(); rows = tbody.find('tr'); expect(rows.length).toBe(7); params.total(20); scope.$digest(); rows = tbody.find('tr'); expect(rows.length).toBe(7); }); it('should show data-title-text', function() { var tbody = elm.find('tbody'); var params = new NgTableParams({}, { dataset: dataset }); scope.tableParams = params; scope.$digest(); var filterRow = angular.element(elm.find('thead').find('tr')[1]); var filterCells = filterRow.find('th'); expect(angular.element(filterCells[0]).attr('data-title-text').trim()).toBe('Name of person'); expect(angular.element(filterCells[1]).attr('data-title-text').trim()).toBe('Age'); expect(angular.element(filterCells[2]).attr('data-title-text').trim()).toBe('Money'); var dataRows = elm.find('tbody').find('tr'); var dataCells = angular.element(dataRows[0]).find('td'); expect(angular.element(dataCells[0]).attr('data-title-text').trim()).toBe('Name of person'); expect(angular.element(dataCells[1]).attr('data-title-text').trim()).toBe('Age'); expect(angular.element(dataCells[2]).attr('data-title-text').trim()).toBe('Money'); }); it('should show/hide columns', function() { var tbody = elm.find('tbody'); scope.tableParams = new NgTableParams({}, { dataset: dataset }); scope.$digest(); var headerRow = angular.element(elm.find('thead').find('tr')[0]); expect(headerRow.find('th').length).toBe(3); var filterRow = angular.element(elm.find('thead').find('tr')[1]); expect(filterRow.find('th').length).toBe(3); var dataRow = angular.element(elm.find('tbody').find('tr')[0]); expect(dataRow.find('td').length).toBe(3); scope.showName = false; scope.$digest(); expect(headerRow.find('th').length).toBe(2); expect(filterRow.find('th').length).toBe(2); expect(dataRow.find('td').length).toBe(2); expect(angular.element(headerRow.find('th')[0]).text().trim()).toBe('Age'); expect(angular.element(headerRow.find('th')[1]).text().trim()).toBe('Money'); expect(angular.element(filterRow.find('th')[0]).find('input').length).toBe(0); expect(angular.element(filterRow.find('th')[1]).find('select').length).toBe(1); }); }); describe('title-alt', function() { var elm; beforeEach(inject(function($compile) { elm = angular.element( '' + '' + '' + '' + '' + '' + '
{{user.name}}{{user.age}}{{user.money}}
'); $compile(elm)(scope); scope.$digest(); var params = new NgTableParams({}, { dataset: dataset }); scope.tableParams = params; scope.$digest(); })); it('should show as data-title-text', inject(function($compile) { var filterRow = angular.element(elm.find('thead').find('tr')[1]); var filterCells = filterRow.find('th'); expect(angular.element(filterCells[0]).attr('data-title-text').trim()).toBe('Name'); expect(angular.element(filterCells[1]).attr('data-title-text').trim()).toBe('Age'); expect(angular.element(filterCells[2]).attr('data-title-text').trim()).toBe('£'); var dataRows = elm.find('tbody').find('tr'); var dataCells = angular.element(dataRows[0]).find('td'); expect(angular.element(dataCells[0]).attr('data-title-text').trim()).toBe('Name'); expect(angular.element(dataCells[1]).attr('data-title-text').trim()).toBe('Age'); expect(angular.element(dataCells[2]).attr('data-title-text').trim()).toBe('£'); })); }); describe('sorting', function() { it('should provide $column definition', inject(function($compile) { var columnDef; var elm = angular.element( '' + '' + '' + '' + '
{{user.age}}
'); scope.captureColumn = function($column){ columnDef = $column; return 'age' }; $compile(elm)(scope); scope.$digest(); expect(columnDef).toBeDefined(); })); it('should apply initial sort', inject(function ($compile) { var elm = angular.element( '' + '' + '
{{user.age}}
'); $compile(elm)(scope); var actualSort; scope.tableParams = new NgTableParams({ sorting: { age: 'desc' } }, { getData: function($defer, params){ actualSort = params.sorting(); $defer.resolve([]); }}); scope.$digest(); expect(actualSort.age).toBe('desc'); })); it('when sorting changes should trigger reload of table', inject(function ($compile) { var elm = angular.element( '' + '' + '
{{user.age}}
'); $compile(elm)(scope); var params = createNgTableParams(); scope.tableParams = params; scope.$digest(); params.settings().getData.calls.reset(); params.sorting()['age'] = 'desc'; scope.$digest(); expect(params.settings().getData.calls.count()).toBe(1); params.sorting()['age'] = 'asc'; scope.$digest(); expect(params.settings().getData.calls.count()).toBe(2); // setting the same sort order should not trigger reload params.sorting({ age: 'asc'}); scope.$digest(); expect(params.settings().getData.calls.count()).toBe(2); })); }); describe('paging', function() { var elm; beforeEach(inject(function($compile) { elm = angular.element( '' + '' + '' + '' + '
{{user.age}}
'); dataCallCount = 0; $compile(elm)(scope); scope.$digest(); })); function verifyPageWas(expectedPage){ expect(scope.tableParams.settings().getData.calls.argsFor(0)[0].page()).toBe(expectedPage); } it('should use initial NgTableParams constructor value', function(){ var params = createNgTableParams({ page: 2}, null); scope.tableParams = params; scope.$digest(); verifyPageWas(2); expect(params.settings().getData.calls.count()).toBe(1); }); it('should use initial NgTableParams constructor value combined with filter', function(){ var params = createNgTableParams({ page: 2, filter: { age: 5}}, null); scope.tableParams = params; scope.$digest(); verifyPageWas(2); expect(params.settings().getData.calls.count()).toBe(1); }); it('changing page # should trigger reload of data', function(){ var params = createNgTableParams({ page: 3}, null); scope.tableParams = params; scope.$digest(); verifyPageWas(3); params.settings().getData.calls.reset(); scope.tableParams.page(5); scope.$digest(); verifyPageWas(5); }); }); describe('filters', function(){ var $capturedColumn; beforeEach(inject(function() { // stash a reference to $column definition so that its available in asserts scope.captureColumn = function ($column) { $capturedColumn = $column; }; })); describe('filter specified as alias', function(){ var elm, tp; beforeEach(inject(function($compile) { elm = angular.element( '
' + '' + '' + '' + '' + '
{{user.name}}
' + '
'); $compile(elm)(scope); scope.$digest(); // 'text' is a shortcut alias for the template ng-table/filters/text scope.usernameFilter = {username: 'text'}; tp = scope.tableParams = createNgTableParams({ filterOptions: { filterDelay: 10 } }); scope.$digest(); })); it('should render named filter template', function() { var inputs = elm.find('thead').find('tr').eq(1).find('th').find('input'); expect(inputs.length).toBe(1); expect(inputs.eq(0).attr('type')).toBe('text'); expect(inputs.eq(0).attr('ng-model')).not.toBeUndefined(); expect(inputs.eq(0).attr('name')).toBe('username'); }); it('should databind ngTableParams.filter to filter input', function () { scope.tableParams.filter()['username'] = 'my name is...'; scope.$digest(); var input = elm.find('thead').find('tr').eq(1).find('th').find('input'); expect(input.val()).toBe('my name is...'); }); it('should make filter def available on $column', function () { expect($capturedColumn).toBeDefined(); expect($capturedColumn.filter).toBeDefined(); expect($capturedColumn.filter()['username']).toBe('text'); }); it('when filter changes should trigger reload of table', inject(function ($timeout) { tp.settings().getData.calls.reset(); tp.filter()['username'] = 'new value'; scope.$digest(); $timeout.flush(); // trigger delayed filter tp.filter()['username'] = 'another value'; scope.$digest(); $timeout.flush(); // trigger delayed filter expect(tp.settings().getData.calls.count()).toBe(2); // same value - should not trigger reload tp.filter()['username'] = 'another value'; scope.$digest(); try{ $timeout.flush(); // trigger delayed filter } catch (ex) { } expect(tp.settings().getData.calls.count()).toBe(2); })); it('when filter changes should reset page number to 1', inject(function ($timeout) { // trigger initial load of data so that subsequent changes to filter will trigger reset of page # tp.filter()['username'] = 'initial value'; scope.$digest(); $timeout.flush(); // trigger delayed filter // set page to something other than 1 tp.page(5); expect(tp.page()).toBe(5); // checking assumptions // when tp.filter()['username'] = 'new value'; scope.$digest(); $timeout.flush(); // trigger delayed filter expect(tp.page()).toBe(1); })); }); describe('filter specified with url', function(){ var elm; beforeEach(inject(function($compile) { elm = angular.element( '
' + '' + '' + '' + '' + '' + '
{{user.age}}
' + '
'); $compile(elm)(scope); scope.tableParams = createNgTableParams(); scope.$digest(); })); it('should render filter template specified by url', function() { var inputs = elm.find('thead').find('tr').eq(1).find('th').find('input'); expect(inputs.length).toBe(1); expect(inputs.eq(0).attr('type')).toBe('number'); expect(inputs.eq(0).attr('id')).toBe('age'); }); }); describe('multiple filter inputs', function(){ var elm; beforeEach(inject(function($compile) { elm = angular.element( '
' + '' + '' + '' + '' + '
{{user.name}}
' + '
'); $compile(elm)(scope); scope.$digest(); scope.tableParams = createNgTableParams(); scope.$digest(); })); it('should render filter template for each key/value pair ordered by key', function() { var inputs = elm.find('thead').find('tr').eq(1).find('th').find('input'); expect(inputs.length).toBe(2); expect(inputs.eq(0).attr('type')).toBe('text'); expect(inputs.eq(0).attr('ng-model')).not.toBeUndefined(); expect(inputs.eq(1).attr('type')).toBe('text'); expect(inputs.eq(1).attr('ng-model')).not.toBeUndefined(); }); it('should databind ngTableParams.filter to filter inputs', function () { scope.tableParams.filter()['name'] = 'my name is...'; scope.tableParams.filter()['age'] = '10'; scope.$digest(); var inputs = elm.find('thead').find('tr').eq(1).find('th').find('input'); expect(inputs.eq(0).val()).toBe('my name is...'); expect(inputs.eq(1).val()).toBe('10'); }); it('should make filter def available on $column', function () { expect($capturedColumn).toBeDefined(); expect($capturedColumn.filter).toBeDefined(); expect($capturedColumn.filter()['name']).toBe('text'); expect($capturedColumn.filter()['age']).toBe('text'); }); }); describe('dynamic filter', function(){ var elm, ageFilter; beforeEach(inject(function($compile) { ageFilter = {age: 'text'}; elm = angular.element( '
' + '' + '' + '' + '' + '' + '' + '
{{user.name}}{{user.age}}
' + '
'); $compile(elm)(scope); scope.$digest(); scope.getFilter = function(colDef){ if (colDef.id === 0) { return {username: 'text'}; } else if (colDef.id === 1) { return ageFilter; } }; scope.tableParams = createNgTableParams(); scope.$digest(); })); it('should render named filter template', function() { var usernameInput = elm.find('thead').find('tr').eq(1).find('th').eq(0).find('input'); expect(usernameInput.attr('type')).toBe('text'); expect(usernameInput.attr('name')).toBe('username'); var ageInput = elm.find('thead').find('tr').eq(1).find('th').eq(1).find('input'); expect(ageInput.attr('type')).toBe('text'); expect(ageInput.attr('name')).toBe('age'); }); it('should databind ngTableParams.filter to filter input', function () { scope.tableParams.filter()['username'] = 'my name is...'; scope.tableParams.filter()['age'] = '10'; scope.$digest(); var usernameInput = elm.find('thead').find('tr').eq(1).find('th').eq(0).find('input'); expect(usernameInput.val()).toBe('my name is...'); var ageInput = elm.find('thead').find('tr').eq(1).find('th').eq(1).find('input'); expect(ageInput.val()).toBe('10'); }); it('should render new template as filter changes', function() { ageFilter.age = 'number'; scope.$digest(); var ageInput = elm.find('thead').find('tr').eq(1).find('th').eq(1).find('input'); expect(ageInput.attr('type')).toBe('number'); expect(ageInput.attr('name')).toBe('age'); }); }); describe('filter with placeholder value and alias', function(){ var elm, tp; beforeEach(inject(function($compile) { elm = angular.element( '
' + '' + '' + '' + '' + '
{{user.name}}
' + '
'); $compile(elm)(scope); scope.$digest(); // 'text' is a shortcut alias for the template ng-table/filters/text scope.usernameFilter = { username: { id: 'text', placeholder: 'User name'} }; tp = scope.tableParams = createNgTableParams(); scope.$digest(); })); it('should render named filter template with placeholder value', function() { var inputs = elm.find('thead').find('tr').eq(1).find('th').find('input'); expect(inputs.length).toBe(1); expect(inputs.eq(0).attr('type')).toBe('text'); expect(inputs.eq(0).attr('ng-model')).not.toBeUndefined(); expect(inputs.eq(0).attr('name')).toBe('username'); expect(inputs.eq(0).attr('placeholder')).toBe('User name'); }); it('should databind placeholder value to filter input', function () { scope.usernameFilter.username.placeholder = 'Name of user'; scope.$digest(); var input = elm.find('thead').find('tr').eq(1).find('th').find('input'); expect(input.attr('placeholder')).toBe('Name of user'); }); it('should make filter def available on $column', function () { expect($capturedColumn).toBeDefined(); expect($capturedColumn.filter).toBeDefined(); expect($capturedColumn.filter()).toBe(scope.usernameFilter); }); }); describe('filter with placeholder value and url', function(){ var elm, tp; beforeEach(inject(function($compile) { elm = angular.element( '
' + '' + '' + '' + '' + '
{{user.age}}
' + '
'); $compile(elm)(scope); scope.$digest(); scope.ageFilter = { age: { id: 'ng-table/filters/number.html', placeholder: 'User age'} }; tp = scope.tableParams = createNgTableParams(); scope.$digest(); })); it('should render named filter template with placeholder value', function() { var inputs = elm.find('thead').find('tr').eq(1).find('th').find('input'); expect(inputs.length).toBe(1); expect(inputs.eq(0).attr('type')).toBe('number'); expect(inputs.eq(0).attr('ng-model')).not.toBeUndefined(); expect(inputs.eq(0).attr('name')).toBe('age'); expect(inputs.eq(0).attr('placeholder')).toBe('User age'); }); it('should databind placeholder value to filter input', function () { scope.ageFilter.age.placeholder = 'Age of user'; scope.$digest(); var input = elm.find('thead').find('tr').eq(1).find('th').find('input'); expect(input.attr('placeholder')).toBe('Age of user'); }); it('should make filter def available on $column', function () { expect($capturedColumn).toBeDefined(); expect($capturedColumn.filter).toBeDefined(); expect($capturedColumn.filter()).toBe(scope.ageFilter); }); }); }); describe('show-filter', function(){ var elm; beforeEach(inject(function($compile) { elm = angular.element( '
' + '' + '' + '' + '' + '
{{user.age}}
' + '
'); scope.showFilterRow = true; scope.tableParams = createNgTableParams(); $compile(elm)(scope); scope.$digest(); })); it('when true, should display filter row', function() { var filterRow = elm.find('thead').find('tr').eq(1); expect(filterRow.hasClass('ng-table-filters')).toBe(true); expect(filterRow.hasClass('ng-hide')).toBe(false); }); it('when false, should hide filter row', function() { // given scope.showFilterRow = false; // when scope.$digest(); // then var filterRow = elm.find('thead').find('tr').eq(1); expect(filterRow.hasClass('ng-table-filters')).toBe(true); expect(filterRow.hasClass('ng-hide')).toBe(true); }); }); describe('$columns', function(){ var elm, tp; beforeEach(inject(function($compile) { elm = angular.element( '
' + '' + '' + '' + '' + '' + '
{{user.age}}{{user.name}}
' + '
'); $compile(elm)(scope); scope.$digest(); scope.ageFilter = { age: 'text' }; scope.isAgeVisible = true; scope.ageTitle = 'Age'; tp = scope.tableParams = createNgTableParams(); scope.$digest(); })); it('should make $columns available on the scope created for ng-table', function(){ // check that the scope is indeed the one created for out NgTableParams expect(scope.$$childHead.params).toBe(tp); expect(scope.$$childHead.$columns).toBeDefined(); }); it('should NOT polute the outer scope with a reference to $columns ', function(){ expect(scope.$columns).toBeUndefined(); }); it('$scolumns should contain a column definition for each `td` element', function(){ expect(scope.$$childHead.$columns.length).toBe(2); }); it('each column definition should have getters for each column attribute', function(){ var ageCol = scope.$$childHead.$columns[0]; expect(ageCol.title()).toBe('Age'); expect(ageCol.show()).toBe(true); expect(ageCol.filter()).toBe(scope.ageFilter); expect(ageCol.class()).toBe(''); expect(ageCol.filterData).toBeUndefined(); expect(ageCol.groupable()).toBe(false); expect(ageCol.headerTemplateURL()).toBe(false); expect(ageCol.headerTitle()).toBe(''); expect(ageCol.sortable()).toBe(false); expect(ageCol.titleAlt()).toBe(''); var nameCol = scope.$$childHead.$columns[1]; expect(nameCol.title()).toBe('Name'); expect(nameCol.show()).toBe(true); expect(nameCol.filter()).toBe(false); expect(nameCol.class()).toBe(''); expect(nameCol.filterData).toBeUndefined(); expect(nameCol.groupable()).toBe('name'); expect(nameCol.headerTemplateURL()).toBe(false); expect(nameCol.headerTitle()).toBe(''); expect(nameCol.sortable()).toBe('name'); expect(nameCol.titleAlt()).toBe(''); }); it('each column attribute should be assignable', function(){ var ageCol = scope.$$childHead.$columns[0]; ageCol.title.assign(scope.$$childHead, 'Age of person'); expect(ageCol.title()).toBe('Age of person'); expect(scope.ageTitle).toBe('Age of person'); ageCol.show.assign(scope.$$childHead, false); expect(ageCol.show()).toBe(false); expect(scope.isAgeVisible).toBe(false); var newFilter = {age: 'select'}; ageCol.filter.assign(scope.$$childHead, newFilter); expect(ageCol.filter()).toBe(newFilter); expect(scope.ageFilter).toBe(newFilter); ageCol.class.assign(scope.$$childHead, 'amazing'); expect(ageCol.class()).toBe('amazing'); ageCol.groupable.assign(scope.$$childHead, 'age'); expect(ageCol.groupable()).toBe('age'); ageCol.headerTemplateURL.assign(scope.$$childHead, 'some.html'); expect(ageCol.headerTemplateURL()).toBe('some.html'); ageCol.headerTitle.assign(scope.$$childHead, 'wow'); expect(ageCol.headerTitle()).toBe('wow'); ageCol.sortable.assign(scope.$$childHead, 'incredible'); expect(ageCol.sortable()).toBe('incredible'); ageCol.titleAlt.assign(scope.$$childHead, 'really'); expect(ageCol.titleAlt()).toBe('really'); var nameCol = scope.$$childHead.$columns[1]; nameCol.groupable.assign(scope.$$childHead, false); expect(nameCol.groupable()).toBe(false); nameCol.sortable.assign(scope.$$childHead, false); expect(nameCol.sortable()).toBe(false); }); }); describe('groups', function(){ var $capturedColumn; beforeEach(inject(function() { // stash a reference to $column definition so that its available in asserts scope.captureColumn = function ($column) { $capturedColumn = $column; }; })); describe('one groupable column', function(){ var elm, tp; beforeEach(inject(function($compile) { elm = angular.element( '
' + '' + '' + '' + '' + '' + '
{{user.name}}
' + '
'); $compile(elm)(scope); scope.$digest(); tp = scope.tableParams = createNgTableParams(); scope.$digest(); })); it('should not render group row until group assigned', function() { var groupRow = elm.find('thead').find('.ng-table-group-header'); expect(groupRow.length).toBe(0); }); xit('should render group row once group assigned', function() { // todo: not sure why this test is not working as manually testing shows that it does :-( tp.group('name'); scope.$digest(); var groupRow = elm.find('thead').find('.ng-table-group-header'); expect(groupRow.length).toBe(1); }); }); }); describe('internals', function(){ var elm, $timeout; beforeEach(inject(function($compile, _$timeout_) { $timeout = _$timeout_; elm = angular.element( '' + '' + '' + '' + '
{{user.age}}
'); $compile(elm)(scope); scope.$digest(); })); it('should reload when binding a new tableParams to scope', function(){ var tp = createNgTableParams(); scope.tableParams = tp; scope.$digest(); expect(tp.settings().getData.calls.count()).toBe(1); }); it('should reload 1 time when binding a new tableParams that has an initial settings dataset field', function(){ var tp = createNgTableParams({ dataset: [1,2,3] }); scope.tableParams = tp; scope.$digest(); expect(tp.settings().getData.calls.count()).toBe(1); }); it('should reload 1 time when binding a new tableParams with initial filter that has an initial settings dataset field', function(){ var tp = createNgTableParams({filter: {age: 1}}, { dataset: [1,2,3] }); scope.tableParams = tp; scope.$digest(); expect(tp.settings().getData.calls.count()).toBe(1); }); it('should reload when binding a new tableParams to scope multiple times', function(){ var tp1 = createNgTableParams(); scope.tableParams = tp1; scope.$digest(); expect(tp1.settings().getData.calls.count()).toBe(1); var tp2 = createNgTableParams(); scope.tableParams = tp2; scope.$digest(); expect(tp2.settings().getData.calls.count()).toBe(1); }); it('should reload 1 time when binding a new settings dataset value and changing the filter', function(){ // given var tp = createNgTableParams({filterOptions: { filterDelay: 100 }, dataset: [{age: 1}, {age: 2}]}); scope.tableParams = tp; scope.$digest(); tp.settings().getData.calls.reset(); // when tp.filter({ age: 1 }); tp.settings({ dataset: [{ age: 1 }, { age: 11 }, { age: 22 }]}); scope.$digest(); $timeout.flush(); // trigger the delayed reload expect(tp.settings().getData.calls.count()).toBe(1); }); it('should reload 1 time when multiple filter changes are debounced', function(){ // given var tp = createNgTableParams({filterOptions: { filterDelay: 100 }, dataset: [{age: 1}, {age: 2}]}); scope.tableParams = tp; scope.$digest(); tp.settings().getData.calls.reset(); // when tp.filter({ age: 1 }); scope.$digest(); tp.filter({ age: 2 }); scope.$digest(); $timeout.flush(); // trigger the delayed reload expect(tp.settings().getData.calls.count()).toBe(1); }); it('should reload 1 time when initial load fails', inject(function($q){ // given var tp = createNgTableParams({ getData: function(){ return $q.reject('BANG!'); }}); // when scope.tableParams = tp; scope.$digest(); // then expect(tp.settings().getData.calls.count()).toBe(1); })); it('should reload 1 time with page reset to 1 when binding a new settings dataset value and changing the filter', function(){ var settings = { counts: [1], dataset: [{age: 1}, {age: 2}], filterOptions: { filterDelay: 100 } }; var tp = createNgTableParams({ count: 1, page: 2 }, settings); scope.tableParams = tp; scope.$digest(); expect(tp.page()).toBe(2); // checking assumptions tp.settings().getData.calls.reset(); // when tp.filter({ age: 1 }); tp.settings({ dataset: [{ age: 1 }, { age: 11 }, { age: 22 }]}); scope.$digest(); $timeout.flush(); // trigger the delayed reload expect(tp.settings().getData.calls.count()).toBe(1); expect(tp.page()).toBe(1); }); it('changing filter, orderBy, or page and then calling reload should not invoke getData twice', function(){ var tp = createNgTableParams(); scope.tableParams = tp; scope.$digest(); tp.settings().getData.calls.reset(); // when tp.filter({ age: 5 }); tp.reload(); scope.$digest(); // then expect(tp.settings().getData.calls.count()).toBe(1); }); it('change to filter that fails to load should not cause infinite reload loop', inject(function($q){ var tp = createNgTableParams({ getData: function(){ if (tp.settings().getData.calls.count() > 1){ return $q.reject('BANG!'); } return [1,2] }}); scope.tableParams = tp; scope.$digest(); expect(tp.settings().getData.calls.count()).toBe(1); // checking assumptions expect(tp.isDataReloadRequired()).toBe(false); // checking assumptions // when tp.filter({ age: 5 }); expect(tp.isDataReloadRequired()).toBe(true); // checking assumptions scope.$digest(); // then expect(tp.isDataReloadRequired()).toBe(false); expect(tp.settings().getData.calls.count()).toBe(2); })); it('changing filter, orderBy, or page in a callback to reload should re-invoke getData 1 time only', function(){ var tp = createNgTableParams(); scope.tableParams = tp; scope.$digest(); tp.settings().getData.calls.reset(); // when tp.filter({ age: 5 }); tp.reload().then(function(){ tp.sorting({ age: 'desc'}); // note: better to call tp.reload() here rather than rely on a watch firing later to do it for us // that way the second reload is chained to the first and returned as a single promise }); scope.$digest(); // then // ie calls.count() === (1 x reload) + (1 x sorting) expect(tp.settings().getData.calls.count()).toBe(2); }); it('changing filter, orderBy, or page then reload in a callback to reload should re-invoke getData 1 time only', function(){ // todo: refactor the watches in ngTableController to handle this case var tp = createNgTableParams(); scope.tableParams = tp; scope.$digest(); tp.settings().getData.calls.reset(); // when tp.filter({ age: 5 }); tp.reload().then(function(){ tp.sorting({ age: 'desc'}); return tp.reload(); }); scope.$digest(); // then // ie calls.count() === (1 x reload) + (1 x sorting) expect(tp.settings().getData.calls.count()).toBe(2); }); it('should not reload when filter value is assigned the same value', function(){ // given var tp = createNgTableParams({ filter: {age: 10} }, { }); scope.tableParams = tp; scope.$digest(); tp.settings().getData.calls.reset(); // when tp.filter({ age: 10}); scope.$digest(); expect(tp.settings().getData.calls.count()).toBe(0); }); it('should reload when filter value changes', function(){ // given var tp = createNgTableParams({ filter: {age: 10} }, {}); scope.tableParams = tp; scope.$digest(); tp.settings().getData.calls.reset(); // when tp.filter({ age: 12}); scope.$digest(); expect(tp.settings().getData.calls.count()).toBe(1); }); it('should reload when new dataset supplied', function(){ // given var initialDataset = [ {age: 1}, {age: 2} ]; var tp = createNgTableParams(); scope.tableParams = tp; scope.$digest(); tp.settings().getData.calls.reset(); // when tp.settings({ dataset: [{ age: 10}, { age: 11}, { age: 12}]}); scope.$digest(); expect(tp.settings().getData.calls.count()).toBe(1); }); }); });