I'm experiencing problems with the Jasmine (ver. 2.4) JavaScript testing framework.
I'm creating a course on udemy.com, but the online checker won't accept the last part of my code, where I'm using the "expect(variable).toBe(true);" function of Jasmine.
describe('A div with class', function() {
var parent = document.getElementsByClassName('media')[0];
it('"media" should exist.', function() {
expect(parent).toBeDefined();
});
var child = document.getElementsByClassName('media-body')[0];
it('"media-body" should exist.', function() {
expect(child).toBeDefined();
});
});
describe('A div with class "media"', function() {
var parent = document.getElementsByClassName('media')[0];
var child = document.getElementsByClassName('media-body')[0];
var answer;
if (parent.firstChild == child) {
answer = true;
} else {
answer = false;
}
it('should have a div with class "media-body" as its child.', function() {
expect(answer).toBe(true);
});
});
My test output from the udemy.com page is:
"A div with class "media" should have a div with class "media-body" as its child.
Expected false to be true"
My updated code which now works very well:
describe('A div with the class', function() {
var class1 = 'media';
var failClass1 = '"' + class1 + '"' + ' should exist.'
var class2 = 'media-body';
var failClass2 = '"' + class2 + '"' + ' should exist.'
var element1 = null;
var element2 = null;
var relationship1_2 = null;
var failRelationship = '"' + class2 + '"' + ' should be nested in a div with the class "' + class1 + '".'
beforeEach(function() {
element1 = document.getElementsByClassName(class1)[0];
element2 = document.getElementsByClassName(class2)[0];
relationship1_2 = element1.contains(element2);
})
it(failClass1, function() {
expect(element1).toBeDefined();
});
it(failClass2, function() {
expect(element2).toBeDefined();
});
it(failRelationship, function() {
expect(relationship1_2).toBe(true);
});
});
I'd recommend re-organize it using beforeEach blocks as the following example:
describe('testing media tag hierarchy', function() {
var parent = null;
beforeEach(function() {
parent = document.getElementsByClassName('media')[0];
})
it('"media" should exist.', function() {
expect(parent).toBeDefined();
});
it('should have a div with class "media-body" as its child.', function() {
expect(parent.firstChild.hasClass('media-body')).toBeTruthy();
});
});
I'm not sure this will fix your problem but it might... it's easier to read.
Related
Hallo i am trying to show a custome view after draging document to my screen,my code is working without bugs but the problem i could not have any update until the page redirecting to other page cause after drag a file.
i am redirecting my page to other page if document draged and my code worked corectly in other module but i do not get why in this module it does not response until the page is redirecting .....
<div ngf-drop ngf-select ng-model="files" ngf-multiple="true" ngf-allow-dir="true" ng-if="dropIsVisible === true">
<div class="drop-area-full-page">
<div class="drop-area-full-page__graphic"></div>
<div class="drop-area-full-page__info" id="drop-area-full-page__info" ng-bind-html="dropText"></div>
</div>
</div>
$window.addEventListener("dragenter", function (e) {
if (isFile(e)) {
lastTarget = e.target;
$scope.dropIsVisible = true;
name = getName($scope, getParent());
$scope.dropText =
"<b> Dokument ablegen zu </b>" + "<b>" + name+ "</b>";
}
});
$window.addEventListener("dragleave", function (e) {
e.preventDefault();
if (e.target === document || e.target === lastTarget) {
$scope.dropIsVisible = false;
}
});
$window.addEventListener("dragover", function (e) {
e.preventDefault();
$scope.dropIsVisible = true;
});
function getParent() {
return {
entityName: $stateParams.entity,
id: $scope.parentId
};
}
$window.addEventListener("drop", function (e) {
e.preventDefault();
$scope.dropIsVisible = true;
var qs = e.dataTransfer.files[0].name;
var parent = getParent();
DokumentUploadMixin.Prepare(qs, e.dataTransfer.files[0], $scope, parent, projection, qs);
//$window.location.href = routeUtils.getCreateDokumentUrl("Dokument", getParent(), projection, qs);
});
};
function isFile(evt) {
var dt = evt.dataTransfer;
for (var i = 0; i < dt.types.length; i++) {
if (dt.types[i] === "Files") {
return true;
}
}
return false;
}
and as i wrote this code worked in other module but in this module it work just after the page start redirecting to other page....
any help to let ng-if respond ?
You added custom event listeners, which are not tracked by Angularjs. In order to make it work you have to cover everything inside addEventListener callback with $scope.$apply to tell angular update the model.
$window.addEventListener("dragenter", function (e) {
$scope.$apply(function() {
if (isFile(e)) {
lastTarget = e.target;
$scope.dropIsVisible = true;
name = getName($scope, getParent());
$scope.dropText =
"<b> Dokument ablegen zu </b>" + "<b>" + name+ "</b>";
}
});
});
$window.addEventListener("dragleave", function (e) {
$scope.$apply(function() {
e.preventDefault();
if (e.target === document || e.target === lastTarget) {
$scope.dropIsVisible = false;
}
});
});
$window.addEventListener("dragover", function (e) {
$scope.$apply(function() {
e.preventDefault();
$scope.dropIsVisible = true;
});
});
Here is a great article about angular internals and how it works.
I have some questions regarding memory leaks.
Please take a look at this block of code:
(function ($) {
$.fn.bnftWindow = function (options) {
// Default Settings
var settings = $.extend({
}, options);
// Validate parameters
if (settings.id === undefined || settings.id === null || settings.id === "") {
throw "bnftWindow id option is undefined, null or empty string";
}
// If window container div already exists
if ($("#" + settings.id).length) {
alert("bnftWindow id already exists - will be destroyed and recreated");
// destroy window container div
$("#" + settings.id).remove();
}
// create window container div
$("body").append("<div id='" + settings.id + "'></div>");
var currentInstance = $("#" + settings.id);
return currentInstance.each(function () {
currentInstance.data("bnftWindow", currentInstance);
var widgetId = currentInstance[0].id; //The id of the html element used to create the bnft widget
// Apply settings
$('#' + widgetId).kendoWindow(
settings
);
var dialog = $('#' + widgetId).data("kendoWindow");
currentInstance.destroy = function () {
kendo.destroy($("#" + widgetId));
$("#" + settings.id).remove();
}
});
function onRefresh(e) {
// Always center window first
if (settings.refresh !== undefined) {
settings.refresh(e);
}
var dialog = $("#" + settings.id).data("kendoWindow");
dialog.center();
}
};
}(jQuery));
And this block of code:
function Test()
{
var element = $("#myElement");
element.hide();
var that = this;
this.ids = {
grid: "messagesGrid",
gridButton: "gridButton",
composeWnd: "composeWnd"
};
this.grid = null; // will store the grid later
$("#" + this.ids.gridButton).on("click", function () {
try {
// Do something....
that.init();
}
}
catch (ex) {
alert("Error: " + ex);
}
});
}
Test.prototype.init = function()
{
// Do something else...
var that = this;
setTimeout(function() {
that.createWidget();
}, 500);
}
Test.prototype.createWidget = function()
{
// Do something else...
$("#grid").kendoGrid({ // Some properties here });
// store grid
this.grid = $("#grid").data("kendoGrid");
// Blah blah blah
}
Do the variables currentInstance, widget id, dialog, element, that, this.grid or the event handler causing memory leaks?
If element variable is causing a memory leak, is $("#myElement").hide(); the solution? (besides setting element variable to null after hide).
Thank you in advance.
The following works for me to clean up dialog instances:
_create: function () {
var that = this;
...
that.frmElement = that.wrapper.kendoWindow({
...
close: function (e) {
this.destroy();
that.wrapper.remove("#" + that.options.dialogId);
that.wrapper.empty();
}
...
}).data("kendoWindow");
}
I have the following directive:
app.directive('pagedownAdmin', ['$compile', '$timeout', function ($compile, $timeout) {
var nextId = 0;
var converter = Markdown.getSanitizingConverter();
converter.hooks.chain("preBlockGamut", function (text, rbg) {
return text.replace(/^ {0,3}""" *\n((?:.*?\n)+?) {0,3}""" *$/gm, function (whole, inner) {
return "<blockquote>" + rbg(inner) + "</blockquote>\n";
});
});
return {
restrict: "E",
scope: {
content: "=",
modal: '=modal'
},
template: '<div class="pagedown-bootstrap-editor"></div>',
link: function (scope, iElement, attrs) {
var editorUniqueId;
if (attrs.id == null) {
editorUniqueId = nextId++;
} else {
editorUniqueId = attrs.id;
}
scope.hideDiv = function () {
document.getElementById("wmd-button-bar-" + editorUniqueId).style.display = 'none';
};
scope.showDiv = function () {
document.getElementById("wmd-button-bar-" + editorUniqueId).style.display = 'block';
};
scope;
var newElement = $compile(
'<div>' +
'<div class="wmd-panel">' +
'<div data-ng-hide="modal.wmdPreview == true" id="wmd-button-bar-' + editorUniqueId + '" style="display:none;"></div>' +
'<textarea on-focus="showDiv()" on-blur="hideDiv()" data-ng-hide="modal.wmdPreview == true" class="wmd-input" id="wmd-input-' + editorUniqueId + '" ng-model="content" >' +
'</textarea>' +
'</div>' +
'<div data-ng-show="modal.wmdPreview == true" id="wmd-preview-' + editorUniqueId + '" class="pagedownPreview wmd-panel wmd-preview">test div</div>' +
'</div>')(scope)
;
iElement.append(newElement);
var help = angular.isFunction(scope.help) ? scope.help : function () {
// redirect to the guide by default
$window.open("http://daringfireball.net/projects/markdown/syntax", "_blank");
};
var editor = new Markdown.Editor(converter, "-" + editorUniqueId, {
handler: help
});
var editorElement = angular.element(document.getElementById("wmd-input-" + editorUniqueId));
editor.hooks.chain("onPreviewRefresh", function () {
// wire up changes caused by user interaction with the pagedown controls
// and do within $apply
$timeout(function () {
scope.content = editorElement.val();
});
});
editor.run();
}
}
}]);
Inside I have the showDiv and hideDiv function that would show and hide the page editor's menu when I click in and out of the textarea.
I am passing the functions to an event inside the compile:
//First try
<textarea onfocus="showDiv()" onblur="hideDiv()"></textarea>
When I click inside and outside the textarea I get the errors:
Uncaught ReferenceError: on is not defined
Uncaught ReferenceError: off is not defined
//Second try
<textarea on-focus="showDiv()" on-blur="hideDiv()"></textarea>
When I click in and out of textarea nothing is happening. No errors but not calling the function either.
Can anyone point me to the right direction? Thanks
Instead of using the same scope, instantiate a new scope (scope.$new()) and assign the functions to this new scope. Because otherwise you will override the function-references assigned by the scope-declaration to the scope-object.
var newScope = scope.$new();
newScope.hideDiv = function() {...};
newScope.showDiv = function() {...};
...
var newElement = $compile(...)(newScope);
And to use the function-references given to the original scope (of the directive) you can call those in the new-scopes functions (e.g. function() { scope.hideDiv(); }).
Working plnkr:
http://plnkr.co/edit/nGux3DOsrcPBcmz43p2A?p=preview
https://docs.angularjs.org/api/ng/service/$compile
https://code.angularjs.org/1.3.16/docs/api/ng/type/$rootScope.Scope#$new
Thank you guys for trying to help. I have found what was wrong with my code. I did a very silly/noob mistake. I used on-focus instead of ng-focus and on-blur instead of ng-blur.
I have function making a $http call and displaying some results. After this particular function, I added another function that will make further calls upon the scroll event (using the ng-Infinite-Scroll module).
My problem is that I can't seem to be able to append the results from the second and following calls to the results displayed by the default call.
Here's my code.
$scope.getDetails = function (id) {
$http.get('http://api.discogs.com/artists/' + id).
success(function(data) {
$scope.artist = data;
});
$http.get('http://api.discogs.com/artists/' + id + '/releases?page=1&per_page=12').
success(function(data2) {
$scope.releases = data2.releases;
});
$scope.$watch(function() {
return $scope.artist;
}, function() {
var pos = $scope.artist.name.toLowerCase().indexOf(', the');
if (pos != -1) {
$scope.artist.name = 'The ' + $scope.artist.name.slice(0, pos);
}
});
var _page = 1;
$scope.releases = [];
$scope.loadDetails = function() {
_page++;
console.log(_page);
$http.get('http://api.discogs.com/artists/' + id + '/releases?page=' + _page + '&per_page=12').then(function(data2) {
$scope.releases = data2.releases;
});
};
$scope.clicked = true;
$scope.sliding = true;
}
Where the following function will call the first page and 12 items:
$http.get('http://api.discogs.com/artists/' + id + '/releases?page=1&per_page=12').
success(function(data2) {
$scope.releases = data2.releases;
});
Later on, I trigger the loadDetails function and it'll make the calls correctly (page 2, 3, and so on), and update the $scope.releases, but so far when this happens, instead of displaying the new results, it disappears all completely once the loadDetails function is called.
var _page = 1;
$scope.releases = [];
$scope.loadDetails = function() {
_page++;
console.log(_page);
$http.get('http://api.discogs.com/artists/' + id + '/releases?page=' + _page + '&per_page=12').then(function(data2) {
$scope.releases = data2.releases;
});
};
I assume that instead of redefining $scope.releases, I have to append the results from the second and following calls to it, but I can't see how i'd do this.
Any hints?
Here's a working Plunker.
I think primarily the problem is that id was missing from loadDetails signature, and .then wasn't waiting for $http.get to return the releases. I substituted .success, and it works.
Here is an update of your plunker: http://plnkr.co/edit/gSwRNUn9mBvhWDtIaEzD
$scope.getDetails = function(id) {
$scope._page = 1;
$scope.releases = [];
$http.get('http://api.discogs.com/artists/' + id).
success(function(data) {
$scope.artist = data;
});
$http.get('http://api.discogs.com/artists/' + id + '/releases?page=1&per_page=12').
success(function(data2) {
$scope.releases = data2.releases;
});
$scope.clicked = true;
$scope.sliding = true;
};
$scope.loadDetails = function(id) {
console.log(id);
if (!angular.isUndefined(id)) {
$scope._page++;
console.log($scope._page);
$http.get('http://api.discogs.com/artists/' + id + '/releases?page=' + $scope._page + '&per_page=12').
success(function(data2) {
for (var i = 0; i < data2.releases.length; i++) {
$scope.releases.push(data2.releases[i]);
}
});
}
};
I'm just using a loop and .push to append to the array.
Say I have the following code that iterates over the <span> inside the <div>:
$('#recommendTextArea').children('span').each(function () {
//mentionedFriends.push($(this).html());
var mentionedFriendName = $(this).html();
jQuery.each(friendsList, function () {
if (this.name == mentionedFriendName) {
var facebookId = '#[' + this.id + ']';
}
});
});
I essentially wanted to replace this <span> with the string of facebookId. Is such thing possible? If yes how?
Use replaceWith.
$('#recommendTextArea').children('span').each(function () {
var $span = $(this);
//mentionedFriends.push($(this).html());
var mentionedFriendName = $(this).html();
jQuery.each(friendsList, function () {
if (this.name == mentionedFriendName) {
var facebookId = '#[' + this.id + ']';
$span.replaceWith(facebookId);
}
});
});
EDIT: If you need to deal with special characters, etc. You can do this instead:
$span.replaceWith($("<div>").text(facebookId).contents());