How to check if "should has text" in Jasmine? - javascript

I need to do something like:
expect(theElement.hasText()).toBe(true);
Do you know how can I do it?
I know that there is a "getText" function in protractor, but, how can I use it?
Should I do?:
expect(theElement.getText().lenght > 0).toBe(true);
Thanks!

I find jasmine-matchers library very helpful in terms of additional useful matchers. toBeNonEmptyString() is a perfect fit here (also notice how readable it is):
expect(theElement.getText()).toBeNonEmptyString();
FYI, here is the underlying implementation:
matchers.toBeNonEmptyString = function() {
return matchers.toBeString.call(this) &&
this.actual.length > 0;
};
It is quite reliable: it checks the type and the length.

If you want to check length and don't want to use toBeNonEmpty, then check it in the callback
element(by.id('element_id')).getText().then(function (data) {
expect(data.length).toBeGreaterThan(0);
});

Related

Simplify jQuery code by naming and recalling a repeated snippet

Sorry this question is so "newbie." I'm not entirely sure if what I'm attempting to do is possible, but I thought I would check.
I have a script that finds and operates on many different elements inside of an iframe, like so:
$(window).load(function(){
$('#iframe').contents().find('target1').doSomething1();
$('#iframe').contents().find('target2').doSomething2();
$('#iframe').contents().find('target3').doSomething3();
$('#iframe').contents().find('target4').doSomething4();
$('#iframe').contents().find('target5').doSomething5();
$('#iframe').contents().find('target6').doSomething6();
$('#iframe').contents().find('target7').doSomething7();
$('#iframe').contents().find('target8').doSomething8();
$('#iframe').contents().find('target9').doSomething9();
...
});
What I would like to do, if possible, is make the code shorter and more readable by defining and recalling $('#iframe').contents().find() as a shorter expression of some sort. Something like this:
$(window).load(function(){
function iframe(){
$('#iframe').contents().find()
}
iframe('target1').doSomething1();
iframe('target2').doSomething2();
iframe('target3').doSomething3();
iframe('target4').doSomething4();
iframe('target5').doSomething5();
iframe('target6').doSomething6();
iframe('target7').doSomething7();
iframe('target8').doSomething8();
iframe('target9').doSomething9();
});
Obviously, that example doesn't work. Is what I'm trying to do even possible?
Sure - you just need a function. You were part of the way there, but you need to specify and use a parameter.
Example:
function iframe(target) {
return $('#iframe').contents().find(target);
}
$(window).load(function(){
iframe('target1').doSomething1();
iframe('target2').doSomething2();
iframe('target3').doSomething3();
iframe('target4').doSomething4();
iframe('target5').doSomething5();
iframe('target6').doSomething6();
iframe('target7').doSomething7();
iframe('target8').doSomething8();
iframe('target9').doSomething9();
});
Note that the function can (and probably should) be defined outside of the .load handler. Also, it's very likely you can simplify this even more, but it's hard to tell without knowing what doSomething1, doSomething2, etc., actually do.
This is what a function is for. You're actually much closer than you realize, you just need your function to accept a parameter and return a result:
function iframe(selector){
return $('#iframe').contents().find(selector);
}
keep it simple and silly.
As the one who asked the question said that he is newbie in functions, I suggest this easy-to-understand solution. Then he will know what to do to make shorter and faster after becoming advanced in JQuery.
Otherwise, he will use the advanced solutions without understanding them. Add on that if he get used to use code that he does not exactly know what it actually does.
$(window).load(function(){
iFrame('#id1');
iFreme('#id2');
...
});
function iFrame(x)
{
if(x=='#id1')
{
$('#iframe').contents().find($(x)).doSomething();
}
else if (x=='#id2')
{
$('#iframe').contents().find($(x)).doSomethingElse();
}
...
}
I'd actually argue in favor of the following:
$(window).load(function(){
var $contents = $('#iframe').contents();
$contents.find('target1').doSomething1();
$contents.find('target2').doSomething2();
$contents.find('target3').doSomething3();
$contents.find('target4').doSomething4();
$contents.find('target5').doSomething5();
});
The reason being the find on the contents() is a smaller scope search than the $('#iframe') search. Alternatively you could also do
$(window).load(function(){
var $targets = $('#iframe').contents().find('target1, target2, target3, target4, target5');
$targets.filter('target1').doSomething1();
$targets.filter('target2').doSomething2();
$targets.filter('target3').doSomething3();
$targets.filter('target4').doSomething4();
$targets.filter('target5').doSomething5();
});
This has the added benifit of doing the finds once and then filtering on the result array to find the object to operate on, rather than doing a find on the contents dom for each target to execute on.
$(window).load(function(){
var i = 1,
$contents = $('#iframe').contents();
for (; i < 10; i++) {
$contents.find('target' + i)['doSomething' + i]();
}
});

AngularJS soltuion to determining if any element in array1 exists in array2

I am working on an angularJS widget (my first) and I currently am looking for an angularJS solution to my problem.
Basically I have one array containing a list of string values.
var array1 = [
"Need to Know",
"Test Category 2",
"News"
];
and another array containing another list of string values
var array2 = [
"need to know",
"release notes",
"NEWS"
];
I need a true statement if any element from one array matches any element from the other array. The result also needs to be case insensitive.
Here is my current solution and works great.
angular.module("myWidget", function(...){
// angular code here
})
.service('arrayService', function() {
function arrayToLowerCase(array) {
return array.join("~!~").toLowerCase().split("~!~");
}
function arrayElementIsInArray(array1, array2) {
for (var i in array1) {
if (array2.indexOf(array1[i]) >= 0) {
return true;
}
}
return false;
}
function arrayCompare(array1, array2) {
return arrayElementIsInArray(arrayToLowerCase(array1), arrayToLowerCase(array2));
}
return {
arrayToLowerCase: arrayToLowerCase,
arrayElementIsInArray: arrayElementIsInArray,
arrayCompare: arrayCompare
};
})
the problem is my javascript coders (I primary work in c#) feel there is a more angularJS way to do this but they have brought nothing to the table as a definitive solution. It was suggested that the $filter module might be useful but I didn't see how it would exactly solve my problem.
If I already have the best solution, then awesome. If not please let me know what you think and lets go from there.
Thanks in advance.
EDIT: In response to some of the answers, I felt that I might have misinterpreted my request. What I am asking is there a built in function that angular provides that does this out of the box?
After researching this a bit more; the $filter Module will probably do it with a custom comparater implemented but that seems like way overkill for what I am looking for.
The current responses are all good stuff though. Thanks again!
Absolutely nothing to do with Angular. This is plain data structures and data manipulation. To say there should be a more AngularJS way of doing it would be like saying there should be a more MVC way to add two numbers.
Angular provides no basic data structures and utility set of functions beyond what is available in your browser's native list of array functions, which is different depending on which ECMAScript standard the browser supports.
You may want to look into a library like Lo-Dash for stuff like this (which you can use right along with Angular with no problems) as it's preferable to have proven code for these kind of data manipulations than to constantly have to debug your own.
With Lo-Dash, and remembering the requirement for case-insensitivity:
var array1Lowered = _.map(array1, function (value) { return value.toLowerCase(); });
var anyMatchesBool = _.any(array2, function (value) {
return _.contains(array1Lowered, value);
});
Note that I'm making the assumption that there will be no non-string items in either array.
Lo-Dash normalizes the API so you don't need to worry about what functions each browswer supports. If there's a native function, Lo-Dash will use it because it's faster. If not, Lo-Dash provides an all-JavaScript implementation.
Try this on for size. To me this really has nothing to do with Angular
(function(array1, array2) {
var tlc = function(a) { return a.toLowerCase(); };
array2 = array2.map(tlc);
array1 = array1.map(tlc);
return array1.filter(function(n) {
return array2.indexOf(n) != -1;
}).length > 0;
})(array1, array2);
Using native functions...
var intersection = array1.filter(function(n) {
return array2.indexOf(n) != -1
});
With help from Simplest code for array intersection in javascript

Shortening a javascript checking for CSS property support

EDIT:
I had a script that checked for CSS property support, in this case hyphenation support. It was quite long, but with the help of the respondents it was shortened to this:
var styleEngine = document.documentElement.style;
if ((!('hyphens' in styleEngine)) && (!('MozHyphens' in styleEngine)) && (!('WebkitHyphens' in styleEngine)) && (!('msHyphens' in styleEngine)))
alert('CSS hyphenation is not supported.');
else
alert('CSS hyphenation is supported.');
The reason for this edit, including the title, is to make this post more useful to people to people googling for a Javascript that checks for CSS property and/or value support. I later learned a few things about CSS support and its check which might be of interest. The first thing I learned is that Chrome at this moment (2014) says it supports -webkit-hyphens, but it does not support the most important value auto. That means that we have to check for property value support.
That can be done in two ways. The first is with the new CSS at-rule #supports. That is explained on https://developer.mozilla.org/en-US/docs/Web/CSS/#supports. The other way, with Javascript, is explained on http://ryanmorr.com/detecting-css-style-support/. And in the case of hyphenation, here is a Javascript polyfill for when proper hyphenation is not supported: https://code.google.com/p/hyphenator/.
Extract it to a function:
function hasStyle(prop) {
return prop in document.documentElement.style;
}
Then you can use:
if (!hasStyle('MozHyphens' && !hasStyle('msHyphens')) { ... }
Not too much shorter, but shorter enough (and encapsulated).
To be more concise, you can create another function:
function hasHyphens() {
return hasStyle('MozHypens') || hasStyle('msHyphens');
}
And then:
if (!hasHyphens()) { ... }
What about:
ValuesIntersect(valArray1, valArray2) {
var len = valArray2.length,
i = 0,
found = false;
while (i<len && !found) {
found = valArray2[i] in valArray1;
++i;
}
return found;
}
Use it like this:
if (!ValuesIntersect(['hyphens','MozHyphens', 'WebkitHyphens', 'msHyphens'], document.documentElement.style)) {...}
I find this is best for readability. When you're at that if statement, this clearly shows what you're checking for. The external function need not necessarily be small since it's rarely ever going to be checked. And it's generic enough to work in multiple places.

Attaching an event to multiple elements at one go

Say I have the following :
var a = $("#a");
var b = $("#b");
//I want to do something as such as the following :
$(a,b).click(function () {/* */}); // <= does not work
//instead of attaching the handler to each one separately
Obviously the above does not work because in the $ function, the second argument is the context, not another element.
So how can I attach the event to both the elements at one go ?
[Update]
peirix posted an interesting snippet in which he combines elements with the & sign; But something I noticed this :
$(a & b).click(function () { /* */ }); // <= works (event is attached to both)
$(a & b).attr("disabled", true); // <= doesn't work (nothing happens)
From what you can see above, apparently, the combination with the & sign works only when attaching events...?
The jQuery add method is what you want:
Adds more elements, matched by the given expression, to the set of matched elements
var a = $("#a");
var b = $("#b");
var combined = a.add(b)
Don't forget either that jQuery selectors support the CSS comma syntax. If you need to combine two arbitrary collections, everyone else's suggestions are on the mark, but if it's as simple as doing something to elements with IDs a and b, use $('#a,#b').
This question has already been answered, but I think a simpler more streamlined way to accomplish the same end would be to rely on the similarities between jQuery's and CSS's selector model and just do:
$("#a, #b").click(function () {/* */});
I use this frequently, and have never seen it not work (I can't speak for jQuery versions before 1.3.2 though as I have not tested this there). Hopefully this helps someone someday.
UPDATE: I just reread the thread, and missed the comment you made about having the nodes in question already saved off to variables, but this approach will still work, with one minor tweek. you will want to do:
var a = $("#a");
var b = $("#b");
$(a.selector+", "+b.selector).click(function () {/* */});
One of the cool things that jquery does is that it adds a few jquery specific properties to the node that it returns (selector, which is the original selector used to grab that node is one of them). You may run into some issues with this if the selector you used already contained commas. Its also probably arguable if this is any easier then just using add, but its a fun example of how cool jquery can be :).
You could just put them in an array:
$.each([a, b], function()
{
this.click(function () { });
});
Why don't you use an array? This works on my side:
$([item1, item2]).on('click', function() {
// your logic
});
I just tried messing around with this, and found something very cool:
$(a & b).click(function() { /* WORKS! */ });
supersweet!
Edit: Now I feel really embarrassed for not testing this properly. What this did, was actually to put the click event on everything... Not sure why it does that, though...
You can also make up a class name and give each element that you want to work with that class. Then you can bind the event to all elements sharing that class.
<p><a class="fakeClass" href="#">Click Me!</a></p>
<p><a class="fakeClass" href="#">No, Click Me!</a></p>
<div class="fakeClass">Please, Click Me!</div>
<script type="text/javascript">
$(function () {
$(".fakeClass").on("click", function () {
alert("Clicked!");
});
})
</script>
try this: sweet and simple.
var handler = function() {
alert('hi!');
}
$.each([a,b], function() {
this.click(handler);
}
BTW, this method is not worth the trouble.
If you already know there are just two of these methods, then I guess the best bet would be
a.click(handler);
b.click(handler);
Cheers!
Example:
$("[name=ONE_FIELD_NAME], [name=ANOTHER_FIELD_NAME]").keypress(function(e){alert(e.which);});

How to select special attributes (prefix:name="")?

how do I select special attributes like 'user:name' or 'city:id' using jQuery?
<div user:name="Ropstah"></div>
<span city:id="4"></div>
Javascript
//this works:
alert($('div').attr("user:name")); // alerts 'Ropstah'
//this doesn't work:
alert($('div[user:name]').attr("user:name")); //error
alert($('div[user\\:name]').attr("user:name")); //error even with special character escaping...
This is a bug in jQuery.
You have two options:
Get rid of the : and using "non standard" attributes (honestly, it's not a big deal)
Get more verbose, or use a plugin to get the functionality anyways:
Initially, you might have to do this:
$('div').filter(function() {
return $(this).attr('user:name') !== undefined;
}).whateverElse();
Speed wise this should be fairly close to jQuery's [] selector as that's what it's doing inside the library anyways. It is, of course, more to type every time you want to find an element that has an attribute, so you could write a plugin for it, since jQuery is awesome and lets you do this sort of thing:
$.fn.hasattr = function(attr) {
return this.filter(function() {
return $(this).attr(attr) !== undefined;
});
};
Which would then let you do a much simpler:
$('div').hasattr('user:name').whateverElse();
Or if you wanted to check if the attribute was equal to something, the plugin might be:
$.fn.cmpattr = function(attr, value) {
return this.filter(function() {
return $(this).attr(attr) == value;
});
};
And you could then do:
$('div').cmpattr('user:name', 'Ropstah').whateverElse();
Fix it:
jQuery.expr.match.ATTR = /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+|\w+:\w+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/;
// ^^^^^^^^
EDIT: This is not an official fix, it appears to work quite well though.
I've had quite a few headaches caused by jQuery and its inner-workings... sometimes, I think, it's okay to get stuck in there and fix it yourself. :)
If you're OK with using non-standard properties (which doesn't effect the rendering of your markup in any way.. it's pretty harmless, really) you can do this:
<div nonstandard="harmless" />
$("div[nonstandard='harmless']")
I've used this approach a few times. It doesn't hurt to be pragmatic.

Categories