Add unit-tests for merlin directives

To simplify templates serving the plugin karma-ng-html2js-preprocessor
has been added to Karma and project dependencies.

Change-Id: If947acd2e9d7e64838c31e406d04fa9c1c67c770
Implements: blueprint merlin-unittests
This commit is contained in:
Timur Sufiev 2015-04-23 21:35:51 +03:00
parent 0b92f674a4
commit e9e750ef4c
6 changed files with 265 additions and 45 deletions

View File

@ -26,6 +26,8 @@ describe('workbook model logic', function() {
});
function getWorkflow(workflowID) {
// once workflow is recreated with JSON, old instance is no longer
// valid, thus we need to get it this way
return workbook.get('workflows').getByID(workflowID);
}
@ -62,6 +64,8 @@ describe('workbook model logic', function() {
taskID = 'task1';
function getTask(taskID) {
// once task is recreated with JSON, old instance is no longer
// valid, thus we need to get it this way
return getWorkflow(workflowID).get('tasks').getByID(taskID);
}

View File

@ -15,50 +15,63 @@
*/
module.exports = function (config) {
'use strict';
'use strict';
config.set({
config.set({
port: 9876,
port: 9876,
basePath: '',
basePath: '',
frameworks: ['jasmine'],
frameworks: ['jasmine'],
browsers: [ 'PhantomJS'],
browsers: [ 'PhantomJS'],
plugins: [
'karma-jasmine',
'karma-phantomjs-launcher',
],
plugins: [
'karma-jasmine',
'karma-phantomjs-launcher',
'karma-ng-html2js-preprocessor'
],
files: [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'merlin/static/merlin/js/lib/underscore-min.js',
'merlin/static/merlin/js/merlin.init.js',
'merlin/static/merlin/js/merlin.templates.js',
'merlin/static/merlin/js/merlin.directives.js',
'merlin/static/merlin/js/merlin.filters.js',
'merlin/static/merlin/js/merlin.field.models.js',
'merlin/static/merlin/js/merlin.panel.models.js',
'merlin/static/merlin/js/merlin.utils.js',
'merlin/static/merlin/js/lib/angular-filter.js',
'merlin/static/merlin/js/lib/barricade.js',
'merlin/static/merlin/js/lib/js-yaml.js',
'merlin/test/js/utilsSpec.js',
'merlin/test/js/templatesSpec.js',
'merlin/test/js/filtersSpec.js',
files: [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'merlin/static/merlin/js/lib/underscore-min.js',
'merlin/static/merlin/js/merlin.init.js',
'merlin/static/merlin/js/merlin.templates.js',
'merlin/static/merlin/js/merlin.directives.js',
'merlin/static/merlin/js/merlin.filters.js',
'merlin/static/merlin/js/merlin.field.models.js',
'merlin/static/merlin/js/merlin.panel.models.js',
'merlin/static/merlin/js/merlin.utils.js',
'merlin/static/merlin/js/lib/angular-filter.js',
'merlin/static/merlin/js/lib/barricade.js',
'merlin/static/merlin/js/lib/js-yaml.js',
'merlin/static/merlin/templates/**/*.html',
// 'merlin/static/merlin/templates/fields/*.html',
'merlin/test/js/utilsSpec.js',
'merlin/test/js/templatesSpec.js',
'merlin/test/js/filtersSpec.js',
'merlin/test/js/directivesSpec.js',
'extensions/mistral/static/mistral/js/mistral.init.js',
'extensions/mistral/static/mistral/js/mistral.workbook.models.js',
'extensions/mistral/static/mistral/js/mistral.workbook.controllers.js',
'extensions/mistral/test/js/workbookSpec.js'
],
'extensions/mistral/static/mistral/js/mistral.init.js',
'extensions/mistral/static/mistral/js/mistral.workbook.models.js',
'extensions/mistral/static/mistral/js/mistral.workbook.controllers.js',
'extensions/mistral/test/js/workbookSpec.js'
],
exclude: [
],
preprocessors: {
'merlin/static/merlin/templates/**/*.html': ['ng-html2js']
},
singleRun: true
});
ngHtml2JsPreprocessor: {
stripPrefix: 'merlin',
moduleName: 'preprocessedTemplates'
},
exclude: [
],
singleRun: true
});
};

View File

@ -5,12 +5,19 @@
'use strict';
angular.module('merlin', [])
.run(['merlin.templates', function(templates) {
templates.prefetch('/static/merlin/templates/fields/',
['dictionary', 'frozendict', 'list', 'string', 'text', 'group', 'number',
'choices'
]
);
}])
.config(function($interpolateProvider) {
// Replacing the default angular symbol
// allow us to mix angular with django templates
$interpolateProvider.startSymbol('{$');
$interpolateProvider.endSymbol('$}');
})
// move these 2 values out of run section to change them in unit-tests
.value('fieldTemplatesUrl', '/static/merlin/templates/fields/')
.value('fieldTemplates', ['dictionary', 'frozendict', 'list',
'string', 'text', 'group', 'number', 'choices'])
.run(['merlin.templates', 'fieldTemplatesUrl', 'fieldTemplates',
function(templates, rootUrl, fieldList) {
templates.prefetch(rootUrl, fieldList);
}])
})();

View File

@ -0,0 +1,195 @@
/* Copyright (c) 2015 Mirantis, Inc.
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
*/
describe('merlin directives', function() {
'use strict';
var $compile, $scope, $httpBackend;
beforeEach(function() {
module('merlin', function($provide) {
$provide.value('fieldTemplates', ['number', 'text']);
});
module('preprocessedTemplates');
});
beforeEach(inject(function(_$compile_, _$rootScope_, _$httpBackend_, _$templateCache_) {
$compile = _$compile_;
$scope = _$rootScope_.$new();
$httpBackend = _$httpBackend_;
$httpBackend.whenGET('/static/merlin/templates/fields/text.html').respond(
200, _$templateCache_.get('/static/merlin/templates/fields/text.html'));
$httpBackend.whenGET('/static/merlin/templates/fields/number.html').respond(
200, _$templateCache_.get('/static/merlin/templates/fields/number.html'));
}));
describe('<panel>', function() {
function getPanelHeading(panelElem) {
var div = panelElem.children().children().eq(0);
return div.hasClass('panel-heading') && div;
}
function getPanelRemoveButton(panelElem) {
var iTag = panelElem.find('i').eq(0);
return iTag.hasClass('fa-times-circle') && iTag;
}
function getPanelBody(panelElem) {
var div = panelElem.children().children().eq(1).children();
return div.hasClass('panel-body') && div;
}
function makePanelElem(contents) {
return $compile('<panel ' + contents + '></panel>')($scope);
}
it('shows panel heading when and only when title is passed via attr', function() {
var title = 'My Panel',
element1 = makePanelElem('title="' + title +'"'),
element2 = makePanelElem('');
$scope.$digest();
expect(getPanelHeading(element1).hasClass('ng-hide')).toBe(false);
expect(element1.html()).toContain(title);
expect(getPanelHeading(element2).hasClass('ng-hide')).toBe(true);
});
it('requires both `title` and `removable` to be removable', function() {
var title = 'My Panel',
element1, element2;
element1 = makePanelElem('title="' + title +'" removable="true"');
element2 = makePanelElem('title="' + title +'"');
$scope.$digest();
expect(getPanelRemoveButton(element1).hasClass('ng-hide')).toBe(false);
expect(getPanelRemoveButton(element2).hasClass('ng-hide')).toBe(true);
});
it('with `on-remove`, but without `removable` is not removable', function() {
var title = 'My Panel',
element1, element2;
$scope.remove = function() {};
element1 = makePanelElem(
'title="' + title +'" removable="true" on-remove="remove()"');
element2 = makePanelElem('title="' + title +'" on-remove="remove()"');
$scope.$digest();
expect(getPanelRemoveButton(element1).hasClass('ng-hide')).toBe(false);
expect(getPanelRemoveButton(element2).hasClass('ng-hide')).toBe(true);
});
it('contents are inserted into div.panel-body tag', function() {
var element = $compile('<panel><span class="inner"></span></panel>')($scope);
$scope.$digest();
expect(getPanelBody(element).find('span').hasClass('inner')).toBe(true);
});
});
describe('<collapsible-group>', function() {
function getGroupBody(groupElem) {
var div = groupElem.children().children().eq(1);
return div.hasClass('collapse') && div;
}
function getGroupRemoveBtn(groupElem) {
var div = groupElem.children().children().eq(0).children().eq(2);
return div.hasClass('add-btn') && div;
}
function getGroupAddBtn(groupElem) {
var div = groupElem.children().children().eq(0).children().eq(1);
return div.hasClass('add-btn') && div;
}
function makeGroupElement(contents) {
return $compile(
'<collapsible-group ' + contents + '></collapsible-group>')($scope);
}
it('requires to specify just `on-remove` to make group removable', function() {
var element1, element2;
$scope.remove = function() {};
element1 = makeGroupElement('');
element2 = makeGroupElement('on-remove="remove()"');
$scope.$digest();
expect(getGroupRemoveBtn(element1).hasClass('ng-hide')).toBe(true);
expect(getGroupRemoveBtn(element2).hasClass('ng-hide')).toBe(false);
});
it('requires to specify `on-add` to make group additive', function() {
var element1, element2;
$scope.add = function() {};
element1 = makeGroupElement('');
element2 = makeGroupElement('on-add="add()"');
$scope.$digest();
expect(getGroupAddBtn(element1).hasClass('ng-hide')).toBe(true);
expect(getGroupAddBtn(element2).hasClass('ng-hide')).toBe(false);
});
it('`additive` attribute set explicitly to `false` makes group not additive', function() {
var element;
$scope.add = function() {};
element = makeGroupElement('on-add="add()" additive="false"');
$scope.$digest();
expect(getGroupAddBtn(element).hasClass('ng-hide')).toBe(true);
});
it('contents are inserted into div.collapse tag', function() {
var element = $compile(
'<collapsible-group><span class="inner"></span></collapsible-group>')($scope);
$scope.$digest();
expect(getGroupBody(element).find('span').hasClass('inner')).toBe(true);
});
});
describe('<typed-field>', function() {
function makeFieldElem(contents) {
return $compile(
'<div><typed-field ' + contents + '></typed-field></div>')($scope);
}
it('type of resulting field is determined by `type` attribute', function() {
var element1, element2;
$scope.value1 = {type: 'text'};
$scope.value2 = {type: 'number'};
element1 = makeFieldElem('value="value1" type="{$ value1.type $}"');
element2 = makeFieldElem('value="value2" type="{$ value2.type $}"');
$httpBackend.flush();
$scope.$digest();
expect(element1.html()).toContain('<textarea');
expect(element2.html()).toContain('<input type="number"');
});
it('field is not rendered until the corresponding template has been served', function() {
var element;
$scope.value = {type: 'text'};
element = makeFieldElem('value="value" type="{$ value.type $}"');
expect(element.html()).not.toContain('<textarea');
$httpBackend.flush();
expect(element.html()).toContain('<textarea');
})
});
});

View File

@ -72,7 +72,7 @@ describe('merlin templates', function() {
success, failure;
function processTemplate(fieldName) {
var promise = templates.templateReady(fieldName)
var promise = templates.templateReady(fieldName);
promise.then(function() {
success = true;

View File

@ -26,7 +26,8 @@
"karma": "^0.12.31",
"karma-chrome-launcher": "^0.1.7",
"karma-jasmine": "^0.3.5",
"karma-phantomjs-launcher": "^0.1.4"
"karma-phantomjs-launcher": "^0.1.4",
"karma-ng-html2js-preprocessor": "~0.1"
},
"main": "Gruntfile.js",
"dependencies": {