I am trying to write a browser test using selenium-webdriverjs. When I call the following code snippet, I get Error:Error response: 13.
browser.waitForCondition('var element = document.querySelector(".selector"); var style = document.defaultView.getComputedStyle(element,null); style =' + btnColor ,timeout);
I am waiting for a condition which I would like to get a computed css style from an element obtained from a css selector. Then the computed css style is compared to a variable called btnColor. (I know that it is also possible to do the same thing using a Webdriver JS API method called getComputedCss. However, I am interested in using waitForCondition to achieve the same purpose.)
I would like to know how to properly use waitForCondition to achieve what I want to do as said above and why the code snippet is throwing the error.
Thanks in advance!
I have found my answer to this question.I have made several javascript mistakes in the expression. The following is the code snippet I have used to solve my problem.
browser.waitForCondition('var element = window.document.querySelector(".selector"); var style = window.document.defaultView.getComputedStyle(element,null).getPropertyValue("background-color"); style ="' + btnColor + '"',timeout);
1) In order to use document, you need to call the window object first.
2) In order to get the computed background-color, I need to use the method .getPropertyValue().
3) btnColor contains a string. Therefore I need to put a double-quotation around it for the interpreter to recognize it as a string.
Related
I don't know JavaScript and I want to add some attributes to a video tag.
Is it possible to use JS code that can affect all the videos on one page?
Please see the below code I tried this after some online search not sure if it is correct or not!
I appreciate your help.
function playVideo() {
var elementVar = document.getElementsByTagName("video");
elementVar.setAttribute("autoplay: autoplay" || "loop: loop" || "controls: false") ;
}
You can definitely set attributes on an element with Javascript. However, the setAttribute function needs to be used differently. It takes two arguments:
The name of the attribute
The value for that attribute
So in your case this would be a correct way to set the autoplay attribute:
elementVar.setAttribute("autoplay","autoplay")
Also, I'm not sure what does the or operator (||) is supposed to be doing between the strings. But it looks like you want to set all of those attributes. That needs to be done with 3 calls, like this:
elementVar.setAttribute("autoplay","autoplay")
elementVar.setAttribute("loop","loop")
elementVar.setAttribute("controls","false")
You can definitely use javascript to add attributes to a video tag, but you aren't using the proper syntax.
The proper syntax for setAttribute is:
.setAttribute(attrName, attrValue);
Note that it can also only set one attribute at a time, so you can't do "autoplay: autoplay" || "loop: loop".
Here's the code you probably want:
function playVideo() {
var elementVar = document.getElementsByTagName("video");
elementVar.setAttribute("autoplay", "autoplay");
elementVar.setAttribute("loop", "loop");
elementVar.setAttribute("controls", "false");
}
Given that I need to set an element's selected index with javascript in capybara by the input name...
var element = document.querySelector("select[name='user[user_locations_attributes][0][location_attributes][state]']").selectedIndex = '50';
What is the proper way to interpret this as a string so it can be executed in Capybara with execute_script(function_name_string)? Because I keep getting syntax errors, unsure how to nest the " and ' quotations.
Easiest solution to your question is to use a heredoc
page.execute_script <<~JS
var element = document.querySelector("select[name='user[user_locations_attributes][0][location_attributes][state]']").selectedIndex = '50';
JS
Although if you have need for the element for anything else it's probably nicer to find the element in ruby and then just call execute_script on the element
el = find("select[name='user[user_locations_attributes][0][location_attributes][state]']")
el.execute_script('this.selectedIndex = 50;')
As a related question - is there a reason you're doing this via JS rather than just clicking on the correct option? If you're just scraping a page there's no issue, but if you're actually testing something this basically makes your test invalid since you could potentially be doing things a user couldn't
Since you commented that you are testing, you really shouldn't be doing this via JS, but should instead be using select or select_option. select takes the options string (which you should have - otherwise why have a select element in the first place)
select('the text of option', from: 'user[user_locations_attributes][0][location_attributes][state]')
select_option is called on the option element directly, which can be found in a number of ways, such as
find("select[name='user[user_locations_attributes][0][location_attributes][state]'] option:nth-child(50)").select_option
I have the below javascript to get the UserID from a online form. This script will go through IE DOM Explorer to find the valued. But when I run the script, it is totally ignoring my "If" statement. It is just providing a value for "NewAuthUserID", without considering the "if".
(function () {
var NewAuthUserID = "";
var UserId = $('tr.background-highlight:contains("REQUESTER PROFILE") + tr').children('td:contains("User ID:")+td').text();
if ('tr.background-highlight:contains("NEW AUTHORIZED INDIVIDUAL PROFILE:"') {
var NewAuthUserID = $('td:contains("User ID:")+td:eq(2)').text();
};
alert(UserId);
alert(NewAuthUserID)
})();
Firstly, I'd suggest to check out how the if statement works: https://www.w3schools.com/js/js_if_else.asp
You need the if statement conditional to return true or false. Right now you're TRYING to use jquery to select things but even that has a syntax issues. Not only that but once the syntax is fixed it STILL won't do what you're attempting to do because you're putting something that will always evaluate to true as the conditional. That jquery selector just returns a function, not a boolean like it looks like you're intending to do. Try this:
(function(){
var NewAuthUserID = "";
var UserId=$('tr.background-highlight:contains("REQUESTER PROFILE") + tr').children('td:contains("User ID:")+td').text();
if($('tr.background-highlight').text() == "NEW AUTHORIZED INDIVIDUAL PROFILE:")){
var NewAuthUserID=$('td:contains("User ID:")+td:eq(2)').text();
}
alert(UserId);
alert(NewAuthUserID)
})();
Notice how I'm snagging the text that you're trying to test against with jquery and expressing it with a conditional instead? In this manner, it will return the boolean: true/false which is what you need to get the if statement to trigger.
Also if you check your syntax, you were missing the $() wrapper around your if statement, but you have a string that looked like it was trying to snag text via jquery.
I suggest formatting your code a bit, this always helps to debug.
The problem is you are trying to use a jQuery selector in your if statement, but you didn't include the $ to evaluate jQuery. It's just evaluating a string, wich results in TRUE (basically doing this: if(true)), so the code block is executed.
Try this instead:
javascript: (function() {
var NewAuthUserID = "";
var UserId = $('tr.background-highlight:contains("REQUESTER PROFILE") + tr').children('td:contains("User ID:")+td').text();
if ($('tr.background-highlight:contains("NEW AUTHORIZED INDIVIDUAL PROFILE:"').length > 0) {
var NewAuthUserID = $('td:contains("User ID:")+td:eq(2)').text();
};
alert(UserId);
alert(NewAuthUserID)
})();
EDIT: I added the length > 0 check on the returned object. It's possible to accomplish this with OP's code, he was just missing those two pieces. :contains is not the same as .text() ==.
Off topic response:
The way you manage/select your nodes may require a lot of maintanance in the future and is prone to errors.
For example: tr.background-highlight:contains("REQUESTER PROFILE") + tr
In words: Get me the table-row after a table-row with hilighted background, that contains "REQUESTER PROFILE".
What if you'll have to add a row in between them? what if you'll need to select the row, wether it is hilighted or not? what if further rows will be hilighted in the future, so that this selector ain't uniqu anymore? what if the label changes? maybe even the language? ...
In each of these cases you'll have to revisit (potentially all) your jquery selectors, just because some minor layout changed.
That's not very reliable.
Will you remember that when you'll get asked to do these changes? Maybe someone else will have to do these changes, will he/she know what to look for?
Tell me, do you remember the details/implications/quirks of the work you've done a week ago? not to speak about your work from a few months ago.
Better:
Use "unique" identifier to, well, identify your nodes by their role; and I'm not talking about IDs. Unique within their specific context.
The easiest way would be to use css-classes. Annotating the rows/cells so you can select the very same field as $('.ref-requester-provile .ref-user-id')
This is way more reliable and future-proof than your bulky $('tr.background-highlight:contains("REQUESTER PROFILE") + tr').children('td:contains("User ID:")+td') where your JS needs to know every little detail of your template/markup, and needs to be adapted with every little change.
Why did I prepend these classes with ref-? to distinct them from classes that are meant for styling
If you don't need to style these nodes and need these identifyer solely to reference them in your JS, I'd rather use a data-attribute. Why? Let's sum it up with:
performance: when you need to add/remove these marker; avoid unnecessary render-cycles
A cleaner seperation between style and code: classes are primarily for styling, but we don't style here.
I am trying to get a certain area of data out from ckeditor. In order to do that I use the following code
function get_body_html(){
var email = CKEDITOR.instances['message'].getData();
var before_body = header_to + to + to_subject + subject + subject_body;
var s_index = email.indexOf(before_body)+before_body.length;
var e_index = email.indexOf(body_footer);
return email.substring(s_index,e_index);
}
For some reason that works when I do this on page load
CKEDITOR.instances.message.setData(header_to + to + to_subject+
subject + subject_body + body_text + body_footer);
get_body_html();
it works correctly and gives me the same string that is contained in body_text.
But when I do this
body_text = get_body_html();
CKEDITOR.instances.message.setData(header_to + to + to_subject + subject +
subject_body + body_text + body_footer);
in an onclick function it gets the wrong indexs somehow. Sometimes it can't find the string and returns -1 other times it just gets a weird index that doesn't make sense. These index variations only happen when my code is changed to tackle the problem a different way. So if it is the wrong indices like -5 and 2 then those would continue to be the wrong indices until I made a code change.
There are two facts that you should know about editor.setData.
In some cases it is asynchronous (it depends on the type of editor). That's why it also accepts a callback. Therefore any code that is meant to be executed after setData() should be executed in that callback.
It never is asynchronous before editor is ready. In this period (between editor initialization and instanceReady event) it works in a different mode - it just caches the set value and on getData() it returns exactly that value.
So, as I see on page load you call synchronously setData() and getData() - your function works because you get the value you're expecting to get.
But then, when you try to getData() when editor is already ready you get the HTML parsed, fixed, processed and perhaps differently formatted by CKEditor. I guess that your indexOf() checks are not enough to handle this. You have to rethink your function - e.g. regexp can help.
What also can help is removing htmlwriter plugin, which formats HTML in a way which may make it harder for you to work with it. E.g.:
config.removePlugins = 'htmlwriter';
I was able to get it to work. So the htmlwriter was one of the problems because it must add spaces in between by HTML tags. The other issue I found is that it strips some of the semicolons out in some of the style attributes. Overall CKEditor does a lot of formatting of the source which makes it very hard to index correctly but it's pretty much a trial and error thing. I ended up using the search JavaScript method for strings which can take a regular expression but I used it the same way indexOf would be used so I don't really know if that made a difference or not.
So, I have some code that should do four things:
remove the ".mp4" extension from every title
change my video category
put the same description in all of the videos
put the same keywords in all of the videos
Note: All of this would be done on the YouTube upload page. I'm using Greasemonkey in Mozilla Firefox.
I wrote this, but my question is: how do I change the HTML title in the actual HTML page to the new title (which is a Javascript variable)?
This is my code:
function remove_mp4()
{
var title = document.getElementsByName("title").value;
var new_title = title.replace(title.match(".mp4"), "");
}
function add_description()
{
var description = document.getElementsByName("description").value;
var new_description = "Subscribe."
}
function add_keywords()
{
var keywords = document.getElementsByName("keywords").value;
var new_keywords = prompt("Enter keywords.", "");
}
function change_category()
{
var category = document.getElementsByName("category").value;
var new_category = "<option value="27">Education</option>"
}
remove_mp4();
add_description();
add_keywords();
change_category();
Note: If you see any mistakes in the JavaScript code, please let me know.
Note 2: If you wonder why I stored the current HTML values in variables, that's because I think I will have to use them in order to replace HTML values (I may be wrong).
A lot of things have been covered already, but still i would like to remind you that if you are looking for cross browser compatibility innerHTML won't be enough, as you may need innerText too or textContent to tackle some old versions of IE or even using some other way to modify the content of an element.
As a side note innerHTML is considered from a great majority of people as deprecated though some others still use it. (i'm not here to debate about is it good or not to use it but this is just a little remark for you to checkabout)
Regarding remarks, i would suggest minimizing the number of functions you create by creating some more generic versions for editing or adding purposes, eg you could do the following :
/*
* #param $affectedElements the collection of elements to be changed
* #param $attribute here means the attribute to be added to each of those elements
* #param $attributeValue the value of that attribute
*/
function add($affectedElements, $attribute, $attributeValue){
for(int i=0; i<$affectedElements.length; i++){
($affectedElements[i]).setAttribute($attribute, $attributeValue);
}
}
If you use a global function to do the work for you, not only your coce is gonna be easier to maintain but also you'll avoid fetching for elements in the DOM many many times, which will considerably make your script run faster. For example, in your previous code you fetch the DOM for a set of specific elements before you can add a value to them, in other words everytime your function is executed you'll have to go through the whole DOM to retrieve your elements, while if you just fetch your elements once then store in a var and just pass them to a function that's focusing on adding or changing only, you're clearly avoiding some repetitive tasks to be done.
Concerning the last function i think code is still incomplete, but i would suggest you use the built in methods for manipulating HTMLOption stuff, if i remember well, using plain JavaScript you'll find yourself typing this :
var category = document.getElem.... . options[put-index-here];
//JavaScript also lets you create <option> elements with the Option() constructor
Anyway, my point is that you would better use JavaScript's available methods to do the work instead of relying on innerHTML fpr anything you may need, i know innerHTML is the simplest and fastest way to get your work done, but if i can say it's like if you built a whole HTML page using and tags only instead of using various semantic tags that would help make everything clearer.
As a last point for future use, if you're interested by jQuery, this will give you a different way to manipulate your DOM through CSS selectors in a much more advanced way than plain JavaScript can do.
you can check out this link too :
replacement for innerHTML
I assume that your question is only about the title changing, and not about the rest; also, I assume you mean changing all elements in the document that have "title" as name attribute, and not the document title.
In that case, you could indeed use document.getElementsByName("title").
To handle the name="title" elements, you could do:
titleElems=document.getElementsByName("title");
for(i=0;i<titleElems.length;i++){
titleInner=titleElems[i].innerHTML;
titleElems[i].innerHTML=titleInner.replace(titleInner.match(".mp4"), "");
}
For the name="description" element, use this: (assuming there's only one name="description" element on the page, or you want the first one)
document.getElementsByName("description")[0].value="Subscribe.";
I wasn't really sure about the keywords (I haven't got a YouTube page in front of me right now), so this assumes it's a text field/area just like the description:
document.getElementsByName("keywords")[0].value=prompt("Please enter keywords:","");
Again, based on your question which just sets the .value of the category thingy:
document.getElementsByName("description")[0].value="<option value='27'>Education</option>";
At the last one, though, note that I changed the "27" into '27': you can't put double quotes inside a double-quoted string assuming they're handled just like any other character :)
Did this help a little more? :)
Sry, but your question is not quite clear. What exactly is your HTML title that you are referring to?
If it's an element that you wish to modify, use this :
element.setAttribute('title', 'new-title-here');
If you want to modify the window title (shown in the browser tab), you can do the following :
document.title = "the new title";
You've reading elements from .value property, so you should write back it too:
document.getElementsByName("title").value = new_title
If you are refering to changing text content in an element called title try using innerHTML
var title = document.getElementsByName("title").value;
document.getElementsByName("title").innerHTML = title.replace(title.match(".mp4"), "");
source: https://developer.mozilla.org/en-US/docs/DOM/element.innerHTML
The <title> element is an invisible one, it is only displayed indirectly - in the window or tab title. This means that you want to change whatever is displayed in the window/tab title and not the HTML code itself. You can do this by changing the document.title property:
function remove_mp4()
{
document.title = document.title.replace(title.match(".mp4"), "");
}