I've been trying to configure ng-html2js to work for my simple directive testing (whole project source can be found here).
The relevant pieces of code are:
angular\karma.conf.js
module.exports = function(config) {
config.set({
basePath: './app',
preprocessors: {
'components/**/*.tpl.html': ["ng-html2js"]
},
ngHtml2JsPreprocessor: {
cacheIdFromPath: function (filepath) {
console.info('ngHtml2JsPreprocessor: Loaded template from path "' + filepath + '"');
return filepath;
},
moduleName: 'jasmineTemplates'
},
files: [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'components/**/*.js',
'components/**/*.tpl.html'
],
autoWatch: true,
frameworks: ['jasmine'],
browsers: ['PhantomJS'],
plugins: [
'karma-ng-html2js-preprocessor',
'karma-phantomjs-launcher',
'karma-chrome-launcher',
'karma-firefox-launcher',
'karma-jasmine'
]
});
};
angular\app\components\paginatedTable\paginatedTable.js
var COMPONENT_NAME = 'PaginatedTable';
var EVENTS = {
ORDERED_BY: COMPONENT_NAME + ': Ordered table',
FILTERED_BY: COMPONENT_NAME + ': Filtered rows'
};
angular.module('paginatedTable', [])
.controller('paginatedTableCtrl',
["$scope", "$sce", "$filter",
function($scope, $sce, $filter) {
$scope.getHeader = function (column) {
var header = column.titleTpl ? column.titleTpl : column.title ? column.title : capitalizeFirstLetter(column.value);
var icon = ($scope.sortBy && $scope.sortBy.reverse) ? '▾' : '▴';
var isSelected = ($scope.sortBy && $scope.sortBy.property) === column.value;
return header + ' <span ng-show="' + (isSelected) + '">' + icon + '</span>';
};
$scope.getProperty = function (column, row) {
return column.format ? column.format(row) : row[column.value];
};
$scope.filterRows = function () {
if ($scope.data) {
$scope.rows = $filter('filter')($scope.data, $scope.filterQuery);
setMetadata('rowsCount', $scope.rows.length);
$scope.$emit(EVENTS.FILTERED_BY, {
id: $scope.id,
filterQuery: $scope.filterQuery
});
}
};
$scope.sortByColumn = function (column) {
$scope.sortBy = $scope.sortBy || {};
if ($scope.sortBy.property && $scope.sortBy.property == column.value) {
$scope.sortBy.reverse = !$scope.sortBy.reverse;
} else {
$scope.sortBy.property = column.value;
$scope.sortBy.reverse = column.reverseOrder || false;
}
$scope.sortTable();
};
$scope.sortTable = function (silent) {
if ($scope.rows && $scope.sortBy && $scope.sortBy.property) {
var sortingParameters = {
rows: $scope.rows,
property: $scope.sortBy.property,
reverse: $scope.sortBy.reverse,
// Optional
defaultProperty: $scope.sortBy.default,
defaultReverse: $scope.sortBy.defaultReverse
};
$scope.rows = sortRowsByProperty(sortingParameters);
$scope.currentPage = 0;
if (silent !== true) {
$scope.$emit(EVENTS.ORDERED_BY, {
id: $scope.id,
sortBy: $scope.sortBy
});
}
}
};
$scope.loadPreviousPage = function () {
$scope.currentPage = fixSmallerPage($scope.currentPage - 1);
};
$scope.loadNextPage = function () {
$scope.currentPage = fixLargerPage($scope.currentPage + 1);
};
$scope.loadPage = function (newPage) {
newPage = fixSmallerPage(newPage);
newPage = fixLargerPage(newPage);
$scope.currentPage = newPage;
};
$scope.getTotalPagesArray = function () {
return $scope.pageCount > 0 ? new Array($scope.pageCount) : [];
};
$scope.$watchGroup(['data', 'filterQuery'], function() {
$scope.filterRows();
});
$scope.$watch('rows', function(rows) {
if (rows) {
$scope.pageCount = getPageCount();
$scope.sortTable(true);
}
});
function getPageCount () {
return Math.ceil($scope.rows.length / $scope.pageSize);
}
function fixSmallerPage (newPage) {
return newPage >= 0 ? newPage : 0;
}
function fixLargerPage (newPage) {
return newPage < $scope.pageCount ? newPage : $scope.pageCount;
}
function capitalizeFirstLetter (string) {
return isString(string) && string.length ? string.charAt(0).toUpperCase() + string.slice(1) : '';
}
function sortRowsByProperty (settings) {
if (settings.rows) {
var propertySteps = settings.property.split('.');
settings.rows.sort(function (row_a, row_b) {
var value_a = getRowProperty(row_a, propertySteps),
value_b = getRowProperty(row_b, propertySteps),
reverse = settings.reverse;
if (value_a === value_b) {
if (!settings.defaultProperty || settings.defaultProperty === settings.property) {
return 0;
} else {
value_a = row_a[settings.defaultProperty];
value_b = row_b[settings.defaultProperty];
reverse = settings.defaultReverse;
}
}
return compareProperties(value_a, value_b, reverse);
});
return settings.rows;
}
}
function getRowProperty (row, propertySteps) {
var value = row;
for (var i in propertySteps) {
value = value[propertySteps[i]];
}
return value;
}
function setMetadata(key, value) {
if ($scope.metadata) {
$scope.metadata[key] = value;
}
}
function compareProperties (value_a, value_b, reverse) {
var sign = reverse ? -1 : 1;
if (isString(value_a) || isString(value_b)) {
return sign * compareStrings(value_a, value_b);
}
return sign * (value_a - value_b);
}
function compareStrings (str_a, str_b) {
if (str_a === '') return -1;
if (str_b === '') return 1;
str_a = ('' + str_a).toLowerCase();
str_b = ('' + str_b).toLowerCase();
return ((str_a < str_b) ? -1 : ((str_a > str_b) ? 1 : 0));
}
}
])
.directive('paginatedTable',
function() {
return {
restrict: 'E',
replace: true,
controller : 'paginatedTableCtrl',
templateUrl: getPath() + 'paginatedTable.tpl.html',
scope: {
id: '=',
columns: '=',
data: '=',
pageSize: '=',
sortBy: '=?',
filterQuery: '=?',
numeration: '=?',
scopeVariables: '=scope',
onLoad: '=?',
metadata: '=?'
},
link: function ($scope, element, attributes) {
var objectProperties = ['sortBy', 'metadata'];
bindScopeVariables($scope);
for (var i in objectProperties) {
var property = objectProperties[i];
$scope[property] = $scope[property] || {};
}
if (!$scope.sortBy.property) {
$scope.sortBy.property = $scope.sortBy.default;
$scope.sortBy.reverse = $scope.sortBy.defaultReverse;
}
$scope.currentPage = 0;
$scope.metadata.$scope = $scope;
$scope.filterRows();
if (typeof $scope.onLoad === 'function') $scope.onLoad($scope);
}
};
function bindScopeVariables ($scope) {
var scopeVariables = $scope.scopeVariables;
if (isObject(scopeVariables)) {
var keys = Object.keys(scopeVariables);
for (var i in keys) {
var key = keys[i];
$scope[key] = scopeVariables[key];
}
}
delete $scope.scopeVariables;
}
function getPath () {
var scripts = document.getElementsByTagName('script');
for (var i in scripts) {
var scriptPath = scripts[i].src;
if (scriptPath.indexOf('paginatedTable.js') > -1) {
return scriptPath.substring(0, scriptPath.lastIndexOf('/') + 1);
}
}
}
}
)
.directive('bindCompiledHtml', [
"$compile",
function ($compile) {
return {
restrict: 'A',
link: function($scope, element, attrs) {
$scope.$watch(function () {
return $scope.$eval(attrs.bindCompiledHtml);
}, function (value) {
element.html(value);
$compile(element.contents())($scope);
});
}
}
}
])
.filter('limitToRange', function() {
return function(rows, limits) {
return rows ? rows.slice(+limits[0], +limits[1]) : undefined;
}
});
function isString (object) {
return typeof object === 'string';
}
function isObject (object) {
return typeof object === 'object';
}
angular\app\components\paginatedTable\paginatedTableDirective.spec.js
(function () {
describe('paginatedTable directive', function () {
var $scope, $compile, dummyRows;
beforeEach(function () {
module("jasmineTemplates");
module("paginatedTable");
//module("components/paginatedTable/paginatedTable.tpl.html");
inject(["$compile", "$rootScope",
function ($compile_, $rootScope) {
$scope = $rootScope.$new();
$compile = $compile_;
}
]);
dummyRows = [
{ keyName: 'value0' },
{ keyName: 'value2', drawSolver: '2' },
{ keyName: 'value1' },
{ keyName: 'value2', drawSolver: '1' },
{ keyName: 'value4' }
];
});
describe('link function', function () {
it('should call the onLoad callback if defined', function() {
// Arrange
$scope.onLoad = function () {
console.log('Loaded directive');
}
spyOn(console, 'log');
initializeScopePaginatedTableSettings();
var directive = getCompiledDirective($scope);
dump(directive);
// Act
$scope.$apply();
$httpBackend.flush();
// Assert
expect(console.log).toHaveBeenCalled();
});
});
/*describe('something', function () {
it('should do something', function () {
});
});*/
function initializeScopePaginatedTableSettings() {
$scope.paginatedTable = {
'columns': [
{
value: 'keyName',
title: 'Title',
format: function (row) {
return '<span>' + row.title + '</span>';
}
},
{
value: 'score',
mouseover: 'Sort by score',
reverseOrder: true
},
],
'sortBy': {
property: 'title',
default: 'title'
},
'pageSize': 1,
'rows': dummyRows
}
}
function getCompiledDirective($scope) {
var html = '<paginated-table '
+ 'columns="paginatedTable.columns" '
+ 'data="paginatedTable.rows" '
+ 'sort-by="paginatedTable.sortBy" '
+ 'page-size="paginatedTable.pageSize" '
+ 'numeration="paginatedTable.numeration" '
+ 'filter-query="filterQuery" '
+ 'metadata="paginatedTable.metadata" '
+ 'on-load="onLoad"'
+ '/></paginated-table>';
var compiledDirective = $compile(html)(angular.extend($scope, {}));
return compiledDirective;
}
});
This returns an error of unexpected get request, which should've been solved by importing the "jasmineTemplates" generated by ng-html2js as configured on karma.conf.js.
And the TPL path is the same ng-html2js processed beforehand:
The tree structure, in case it helps, goes like follows:
EDIT: Running karma on Chrome instead of PhantomJS still throws the same error so I'd suppose this isn't PhantomJS specific.


While creating the controller in your test case file, create mocks for all the dependencies in your actual controller. I got a similar error which I fixed by including all the dependencies in test case.