I was studying about TDD and unit testing using Jasmine JS and I have a question about their methods.
I found two methods and would like to know what is the difference.
describe('Teste do toContain', () => {
var name = 'Lucas de Brito Silva'
it('Deve demonstrar o uso do toContain', () => {
expect(name).toContain('Lucas');
});
});
describe('Teste do toMatch', function () {
var text = 'Lucas de Brito Silva'
it('deve validar o uso do toMatch', () => {
expect(text).toMatch('Brito');
});
})
The difference is partly what they operate on but also what they will do.
Here is the example usage from version 2 of Jasmine (but this runs the examples using the latest version):
it("The 'toMatch' matcher is for regular expressions", function() {
var message = "foo bar baz";
expect(message).toMatch(/bar/);
expect(message).toMatch("bar");
expect(message).not.toMatch(/quux/);
});
describe("The 'toContain' matcher", function() {
it("works for finding an item in an Array", function() {
var a = ["foo", "bar", "baz"];
expect(a).toContain("bar");
expect(a).not.toContain("quux");
});
it("also works for finding a substring", function() {
var a = "foo bar baz";
expect(a).toContain("bar");
expect(a).not.toContain("quux");
});
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine-html.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/boot.min.js"></script>
It does demonstrate what they can do.
toContain will work on both arrays and strings. It will essentially be the same using Array#includes or String#includes - the array or string will be checked if it has an item (for arrays) or a subsequence (for strings) that matches the argument. expect(something).toContain(other) will be roughly like checking something.includes(other) === true.
toMatch instead uses a regular expression. So, first of all, it only works on strings, not arrays. Second, if given a string as an argument generates a regex from it. So, expect(something).toMatch(other) will actually be resolved as if new RegExp(other).test(something). This does mean that if you want to use it for simple string matching you should be careful to not use special characters:
it("The 'toMatch' matcher generates a regex from the input", function() {
var message = "foo\\dbar";
expect(message).toMatch(message);
});
it("The generated matcher will obey regex restrictions", function() {
var pattern = "foo\\dbar";
expect(pattern).not.toMatch(pattern);
expect("foo4bar").toMatch(pattern);
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine-html.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/boot.min.js"></script>
Here, the value of the message string is foo\dbar but if you generate a regex from that, then it wouldn't match the same string, since \d denotes a digit - foo4bar will match but not foo\dbar.
Related
I am trying to test the behaviour of a method that produce regex.
Using Mocha/Chai test suite I have the following code :
describe('regexTest',function () {
it('should return a regexp', function () {
var regex = regexTest();
assert.equal(regex, /someregex/);
});
});
But it seems that the code above doesn't work. I tried in the chrome console :
/a/ == /a/
> false
For the moment the only way I found is to compare the toString of the two regex (that should be equals and that I can compare) :
describe('regexTest',function () {
it('should return a regexp', function () {
var regex = regexTest();
assert.equal(regex.toString(), '/someregex/');
});
});
Do you know a better way to do this ? Because I find this not really clean ...
Since regular expression is an object, you need to use deep equals to compare 2 regex objects with each other
expect(/a/).to.be.deep.equal(/a/, 'should match')
Here is how I test if a literal regexp is valid:
var assert = require('assert');
describe('RegExp Test', function() {
it('should return a valid RegExp', function() {
var getSomeRegex = function() {
return /someregex/;
};
var aRegex = getSomeRegex();
assert.equal(aRegex instanceof RegExp, true);
});
it('should return a invalid Regexp', function() {
var getInvalidRegex = function() {
return '/something';
};
var aInvalidRegex = getInvalidRegex();
assert.equal(aInvalidRegex instanceof RegExp, false);
});
});
It's a little late, but for anyone else who stumbles across this on Google way I did, Chai's normal comparison functions work with Regexp objects too...
const re = new RegExp( '^foo.*bar$', 'i' );
re.should.eql( /^foo.*bar$/i );
I've watched some videos on the topic of backbone js. This is an example straight from the video. It is from 2012, so I'm thinking backbone rules/library have changed, but I can't figure out why this does not work at the moment. In the video, the person shows it running in the JS Fiddle, but I can't get it to work. (I've included the necessary libraries in JS Fiddle, i.e. underscore, backbone and jQuery)
var V = Backbone.View.extend({
el:'body',
render: function () {
var data = { lat: -27, lon: 153 };
this.$el.html(_.template('<%= lat %> <%= lon%>', data));
return this;
}
});
var v = new V();
v.render();
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://backbonejs.org/backbone-min.js"></script>
You used to be able to parse and fill in an Underscore template in one go like this:
var html = _.template(template_string, data);
But as of Underscore 1.7.0, the second argument to _.template contains template options:
template _.template(templateString, [settings])
Compiles JavaScript templates into functions that can be evaluated for rendering. [...] The settings argument should be a hash containing any _.templateSettings that should be overridden.
You have to compile the template using _.template and then execute the returned function to get your filled in template:
var tmpl = _.template(template_string);
var html = tmpl(data);
// or as a one-liner, note where all the parentheses are
var html = _.template(template_string)(data);
In your case, it would look something like this:
var V = Backbone.View.extend({
el:'body',
render: function () {
var data = { lat: -27, lon: 153 };
var tmpl = _.template('<%= lat %> <%= lon %>');
this.$el.html(tmpl(data));
return this;
}
});
var v = new V();
v.render();
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.0/backbone-min.js"></script>
This can be useful
1: If you have more then one template or sometime you are using external template so it can be useful for you inside method you can write reusable code
var V = Backbone.View.extend({
el:'body',
temp: function (str) {
// reusable code
return _.template(str);
},
render: function () {
var data = { lat: -27, lon: 153 };
// calling your view method temp
var tmpl = this.temp('<%= lat %> <%= lon %>');
this.$el.html(tmpl(data));
return this;
}
});
var v = new V();
v.render();
We have an ember component (let's call it component B), and the template for that component contains another component (component A). If we have computed properties in component B bound to properties in component A, the bindings are not working completely when we're testing using ember-qunit, but the bindings are working in the real application. In the tests, the bindings are working if we programmatically set values in components A or B, but if we use ember helpers (e.g. fillIn) to set component values, the bindings aren't getting fired. We don't experience this problem with non-nested components.
A jsfiddle that demonstrates the problem is here: http://jsfiddle.net/8WLpx/4/
Please ignore that parent component below could have just been an extension of the nested component. This is just to demonstrate the issue.
Code below if you'd rather:
HTML/handlebars
<!-- URL input -->
<script type="text/x-handlebars" data-template-name="components/url-input">
<div {{ bind-attr class=":input-group showErrors:has-error:" }}>
{{input value=web_url class="form-control"}}
</div>
</script>
<!-- video URL input -->
<script type="text/x-handlebars" data-template-name="components/video-url-input">
{{url-input class=class value=view.value selectedScheme=view.selectedScheme web_url=view.web_url}}
</script>
Component Javascript
//=============================== url input component
App.UrlInputComponent = Ember.Component.extend({
selectedScheme: 'http://',
value: function(key, value, previousValue) {
// setter
if (arguments.length > 1) {
this.breakupURL(value);
}
// getter
return this.computedValue();
}.property('selectedScheme', 'web_url'),
computedValue: function() {
var value = undefined;
var web_url = this.get('web_url');
if (web_url !== null && web_url !== undefined) {
value = this.get('selectedScheme') + web_url;
}
return value;
},
breakupURL: function(value) {
if(typeof value === 'string') {
if(value.indexOf('http://') != -1 || value.indexOf('https://') != -1) {
var results = /^\s*(https?:\/\/)(\S*)\s*$/.exec(value);
this.set('selectedScheme', results[1]);
this.set('web_url', results[2]);
} else {
this.set('web_url', value.trim());
}
}
},
onWebURLChanged: function() {
// Parse web url in case it contains the scheme
this.breakupURL(this.get('web_url'));
}.observes('web_url'),
});
//=============================== video url input component
App.VideoUrlInputComponent = Ember.Component.extend({
value: "http://",
selectedScheme: 'http://',
web_url: "",
});
Test Code
emq.moduleForComponent('video-url-input','Video URL Component', {
needs: ['component:url-input',
'template:components/url-input'],
setup: function() {
Ember.run(function() {
this.component = this.subject();
this.append();
}.bind(this));
},
});
emq.test('Test fill in url programmatically', function() {
var expectedScheme = 'https://';
var expectedWebURL = 'www.someplace.com';
var expectedURL = expectedScheme + expectedWebURL;
Ember.run(function() {
this.component.set('selectedScheme', expectedScheme);
this.component.set('web_url', expectedWebURL);
}.bind(this));
equal(this.component.get('value'), expectedURL, "URL did not match expected");
});
emq.test('Test fill in url via UI', function() {
var expectedURL = 'https://www.someplace.com';
fillIn('input', expectedURL);
andThen(function() {
equal(this.component.get('value'), expectedURL, "URL did not match expected");
}.bind(this));
});
The this.append() cannot happen in the test setup; it must happen in the "test" method because the ember qunit "test" wrapper clears all of the views before calling the standard qunit "test" method.
Given:
var myApp = angular.module('myApp', []);
myApp.directive('myDirective', function () {
return {
restrict: 'E',
scope: {
classToAdd: '#'
},
template:
'<div class="{{classToAdd}}"></div>'
};
});
I'm testing a spec where classToAdd is being statically coded in the template:
<my-directive class-to-add="foo"></my-directive>
and the classToAdd attribute is only being recognized if I $digest $rootScope, and not $scope.
Why this is the case?
Working fiddle.
The reason the shown fiddle was failing, is that "foo" is bound to the $rootScope, not the local $scope.
The solution is to set the scope variable, interpolate it using {{foo}} (since we're using "#" in the isolate scope)
it('should bind to the class-to-add attribute when we $digest $scope', function () {
// Arrange
template = '<my-directive class-to-add="{{foo}}"></my-directive>';
compiledDirective = $compile(template)($scope);
directiveEl = compiledDirective.find('div');
$scope.foo = "bar"
// Act
$scope.$digest();
// Assert
expect(directiveEl.hasClass('bar')).toBe(true);
});
you can write your code like this:
angular.module('app',[])
.controller('ctrl',['$scope',function($scope){
$scope.myClass="myClass";
}])
.directive('myDirective',function(){
return {
restrict:'E',
scope:{ classToAdd: '#' },
transclude:true,
replace: true,
template:'<div class="{{classToAdd}}" ng-transclude></div>',
link: function(scope, iElement, iAttrs){
console.log(scope);
console.log(iAttrs);
}
}
})
and here is a working DEMO
notice the console for logs and under the hood stuff.
I am writing a REST app in Angular and I want to write unit tests for it (of course!). I have a controller which gets a list of blog posts from a REST service in json and puts the summaries into the $scope, so I can display them in the view.
At first the blog posts were just displaying as text ie <p>Blog body</p>, rather than rendering as parsed HTML, until I discovered that you can use ng-bind-html in conjunction with the $sce service. This now works fine in terms of displaying the blog posts correctly.
The problem arises when unit testing. I am trying to mock a json response with some HTML and then test that my controller is correctly dealing with the HTML. Here is my code:
Controller
.controller( 'HomeCtrl', function HomeController( $scope, $http, $sce ) {
$scope.posts = {};
$http.get('../drupal/node.json').success(function (data) {
var posts;
posts = data.list;
for(var i = 0; i < posts.length; i ++) {
posts[i].previewText = $sce.trustAsHtml(posts[i].body.summary);
posts[i].created = posts[i].created + '000'; // add milliseconds so it can be properly formatted
}
$scope.posts = posts;
});
})
unit test
describe('HomeCtrl', function() {
var $httpBackend, $rootScope, $sce, createController;
beforeEach(inject(function ($injector) {
// Set up the mock http service responses
$httpBackend = $injector.get('$httpBackend');
// Get hold of a scope (i.e. the root scope)
$rootScope = $injector.get('$rootScope');
// The $controller service is used to create instances of controllers
var $controller = $injector.get('$controller');
$sce = $injector.get('$sce');
createController = function() {
return $controller('HomeCtrl', {
'$scope': $rootScope
});
};
}));
it('should get a list of blog posts', function() {
var rawResponse = {
"list": [
{
"body": {
"value": "\u003Cp\u003EPost body.\u003C\/p\u003E\n",
"summary": "\u003Cp\u003ESummary.\u003C\/p\u003E\n"
},
"created": "1388415860"
}
]};
var processedResponse = [{
"body": {
"value": "\u003Cp\u003EPost body.\u003C\/p\u003E\n",
"summary": "\u003Cp\u003ESummary.\u003C\/p\u003E\n"
},
"created": "1388415860000",
previewText: $sce.trustAsHtml("\u003Cp\u003ESummary.\u003C\/p\u003E\n")
}];
$httpBackend.when('GET', '../drupal/node.json').respond(rawResponse);
$httpBackend.expectGET("../drupal/node.json").respond(rawResponse);
var homeCtrl = createController();
expect(homeCtrl).toBeTruthy();
$httpBackend.flush();
expect($rootScope.posts).toEqual(processedResponse);
});
});
When I run the above through the Karma test runner, I get the following response:
Chrome 31.0.1650 (Windows) home section HomeCtrl should get a list of blog posts FAILED
Expected [ { body : { value : '<p>Post body.</p>
', summary : '<p>Summary.</p>
' }, created : '1388415860000', previewText : { $$unwrapTrustedValue : Function } } ] to equal [ { body
: { value : '<p>Post body.</p>
', summary : '<p>Summary.</p>
' }, created : '1388415860000', previewText : { $$unwrapTrustedValue : Function } } ].
I suspect the problem is due to the fact that $sce.trustAsHtml returns an object containing a function, rather than a string.
My question is, firstly, am I approaching this problem in the correct way?
Secondly, if so, how should I go about testing the output of $sce.trustAsHtml?
Since the answer given by michael-bromley didn't work for me I want to point out another solution. In my case I was using a filter that wraps each occurrence of a string in another string with a span that has a class of 'highlight'. In other words, I want words to be highlighted. Here is the code:
angular.module('myModule').filter('highlight', function ($sce) {
return function (input, str) {
return $sce.trustAsHtml((input || '').replace(new RegExp(str, 'gi'), '<span class=\"highlighted\">$&</span>'));
};
});
I use the $sce service to trust the resulting value as HTML. To test this I need to use the $$unwrapTrustedValue function on the resulting value to get my test working:
it('01: should add a span with class \'highlight\' around each mathing string.', inject(function ($filter) {
// Execute
var result = $filter('highlight')('this str contains a str that will be a highlighted str.', 'str');
// Test
expect(result.$$unwrapTrustedValue()).toEqual('this <span class="highlighted">str</span> contains a <span class="highlighted">str</span> that will be a highlighted <span class="highlighted">str</span>.');
}));
UPDATE:
As #gugol kindly pointed out it is preferred not to use Angular internal methods like $$unwrapTrustedValue. A better approach is to use the public getTrustedHtml method on the $sce service. Like so:
it('01: should add a span with class \'highlight\' around each mathing string.', inject(function ($sce, $filter) {
// Execute
var result = $filter('highlight')('this str contains a str that will be a highlighted str.', 'str');
// Test
expect($sce.getTrustedHtml(result)).toEqual('this <span class="highlighted">str</span> contains a <span class="highlighted">str</span> that will be a highlighted <span class="highlighted">str</span>.');
}));
You have to disable $sce using its provider before each test.
When $sce is disabled all $sce.trust* methods just return original value instead of a wrapper function.
beforeEach(module(function ($sceProvider) {
$sceProvider.enabled(false);
}));
it('shall pass', inject(function($sce){
expect($sce.trustAsHtml('<span>text</span>')).toBe('<span>text</span>');
}));
In your particular example just do this:
describe('HomeCtrl', function() {
var $httpBackend, $rootScope, $sce, createController;
beforeEach(module(function ($sceProvider) {
$sceProvider.enabled(false);
}));
// rest of the file
});
I discovered that you can use $sce.getTrusted which will return the value originally passed to $sce.trustAsHtml, in this case a string containing HTML, which you can then test for equality in the usual way.
So my test now looks like this:
it('should create a previewText property using $sce.trustAsHtml', function() {
// confirms that it is an object, as should be the case when
// it has been through $sce.trustAsHtml
expect(typeof result.previewText === 'object').toEqual(true);
expect($sce.getTrusted($sce.HTML, result.previewText))
.toEqual('<p>Original HTML content string</p>');
});
Another option is to use the getTrustedHtml() function to get the html string value from $$unwrapTrustedValue.
vm.user.bio = $sce.getTrustedHtml(vm.user.bio);