Protractor: find a text in a page - javascript

I've created this step definition to check if a given text exists in a web page. The code works properly but sometimes, expecially when I look for a long text, I get an error even if the text exists in the page.
The code is this:
this.Then(/^The text "([^"]*)" exists in page$/, function(myText, callback){
browser.sleep(10000);
var selectedElement = element(by.xpath("//*[. = '" + myText + "']"));
expect(selectedElement.isPresent()).to.eventually.equal(true, "This text is not present in page").and.notify(callback);
});
Basically I do three things:
I wait 10 seconds to be sure that all elements of the DOM have been loaded.
I seach for an element(div, p, label or whatever) thet contains the text I'm looking for.
I check if the element is present oterwise I get an error.
Can you tell me if there is a better way to do this? It's correct to use browser.sleep() at the beginning of a step definition to wait for the DOM loading?
IMPORTANT: I'm not using Angularjs
Thank you in advance.

Most likely, the problem is because you are using a strict text match, but there could be extra spaces or newlines around a desired text. You can try with contains() if this is applicable:
element(by.xpath("//*[contains(., '" + myText + "')]"));
Or, with normalize-space():
element(by.xpath("//*[text()[normalize-space() = '" + myText + "']]"));
As a side note, using browser.sleep() to wait for a page to load is not quite reliable and should be avoided. There is a better way - browser.wait() and Expected Conditions.

Related

create a safe placeholder in html

I am creating a helper function that is called by a templating library. My helper generates at the moment a div with a guid id - which is used as a placeholder - as well as a script which calls back to the server every second to get content to put into that placeholder - with essentially this code:
$.get( url + guid).done( result => $("#" +guid).html( result));
so - it all works perfectly - unless my helper function is used inside a table block - in which case, because <div> is not an allowed child of tbody - chrome kicks it out... so when I actually turn up with rows and columns later - they are no longer inside the table block - they turn up above it.
Now, of course the problem is that you get no context in the template renderer - ie it's completely impossible for me to know whether or not I'm in a table.
Can anyone think of any way I can put any sort of placeholder - text, comment, whatever inside my initial rendered html in a way that I can update it multiple times and the new html I insert will always appear exactly in the same place and context the original template instruction was?
You could use the <span></span> element.
Here's a pretty straightforward explanation:
The tag is used to group inline-elements in a document.
The tag provides no visual change by itself.
The tag provides a way to add a hook to a part of a text or a
part of a document.
I found an answer - I'd still like a better one, but if I use comments like <!-- start --> and <!-- stop --> I can replace them with this:
var inner = document.body.innerHTML;
var comment = s => "<!-- " + s + " -->";
var reg = new RegExp( comment("start") + "(.|\n)*" + comment("stop"), "gim");
var replaced = inner.replace( reg, comment("start") + "<tr><td>1</td><td>2</td></tr>"
+ comment("stop"));
document.body.innerHTML = replaced;
being careful to construct the regexp so that it doesn't match itself :) Anyway - not ideal replacing the entire innerhtml - but it's my answer unless anyone can come up with something better. Because comments don't get moved - from anywhere.

How to bind JavaScript event on page load using asp.net C#

I am trying to bind JavaScript event on page load in C# i have tried this code
Response.Write("<li><a href='#' onclick=BindID('" + SubMenu.ParentId + "','" + SubMenu.FormName + "','" + URL + "','" + SubMenu.FormCaption+ "')>" + newSubMenuItem.Text + "</a></li>");
after execution the following output is generated in my html page (on browser).
Copyrights Filing
the variable SubMenu.FormCaption contains string value 'Copyrights filing' but the browser is adding a double-quote when the variable contains a space, and the value becomes 'Copyrights" filing'.
What is the problem with the code?
That because the onclick have to look like:
onclick="BindID(...)"
and yours look like:
onclick=BindID(...)
so simply add quotes before and after
Response.Write("<li>" + newSubMenuItem.Text + "</li>");
so the broswer don't know how to parse it exactly then he guesses hopefully it will work
It's because of missing double quote at the beginning of the BindID method. The browser treats the double quote before url as ending tag of the li element hence gives the error.
It's always better to use string.format method to generate htmls dynamically. It's easy to maintain, read and understand.
like
String.Format("<li><a href='#' onclick="BindID('{0}','{1}','{2}','{3}')>{4}</a></li>", SubMenu.ParentId, SubMenu.FormName, SubMenu.FormCaption,newSubMenuItem.Text);
Got me stumped. I don't have a clue why this is happening.
My answer is more of an alternative approach for you:
var markup = String.Format("<li><a href='#' onclick=BindID('{0}','{1}','{2}','{3}')>{4}</a></li>", SubMenu.ParentId, SubMenu.FormName, SubMenu.FormCaption,newSubMenuItem.Text);
Response.Write(markup);
I'd recommend this anyway depending on the context of your problem (can improve performance).
http://msdn.microsoft.com/en-us/library/system.string.format(v=vs.110).aspx
On page load you can only paste this code. When page will load JavaScript code will run. Change the function name to your own function .
ScriptManager.RegisterStartupScript(Page, Page.GetType(), Guid.NewGuid().ToString(), `"javascript:funclass();", true);`
You are being led up the garden path here by using a browser's inspect element function, in this case Chrome's I suspect, which is giving the browser's best interpretation of the malformed html. In cases like this you should always use View Source to see the raw output. If you do this you'll see that what's output is in fact:
<li><a href='#' onclick=BindID('59','Registration','ApplicationForms/CaseManagment/Case.aspx','Copyrights filing')>Copyrights Filing</a></li>
looking at this, and to be fair on Inspect Element - it probably does help to compare the two, you should be able to spot, as other have pointed out, that onclick attribute's value is not being wrapped in quotes as it needs to be.
The quote mark you see in the middle of 'Copyrights" filing' in the Inspect Element view is the result of the browser terminating the onclick value at the first white space, then wrapping it all up in quotes itself.

Why creating the elements with custom tags adds the xml namespace in the outerHTML in IE9 or 10 until the .find() method is called?

I have a jsfiddle that demonstrates the question:
http://jsfiddle.net/H6gML/8/
$(document).ready(function() {
// this seems fine in IE9 and 10
var $div = $("<div>");
console.log("In IE, this <div> is just fine: " + $div[0].outerHTML);
// this is weird in IE
var $test = $("<test>");
console.log("However, this <test> has an xml tag prepended: \n"
+ $test[0].outerHTML);
$test.find("test");
console.log("Now, it does not: \n" + $test[0].outerHTML);
console.log("Why does this behave this way?");
});
Why does this happen? It doesn't happen in Chrome or Firefox. Is there a better way to fix this than to call .find("test") on the object?
Edit
To clarify, I'm not asking why the xml tag is added, rather, I'm wondering why the .find() call get's rid of it. It doesn't make sense to me.
Why does this happen? It doesn't happen in Chrome or Firefox. Is there a better way to fix this than to call .find("test") on the object
It is the IE causing the issue while doing document.createElement on an unknown html element type. It thinks it is an XML node and adds the xml namespace prefixed <?XML:NAMESPACE PREFIX = PUBLIC NS = "URN:COMPONENT" />. Instead if you try to make it explicit to mention that it is an html element, this issue doesn't happen.
Try:
var $test = $("<html><test/></html>");
The issue no longer occurs.
To clarify, I'm not asking why the xml tag is added, rather, I'm wondering why the .find() call get's rid of it. It doesn't make sense to me.
Now, when you do a find, jquery internally uses context.getElementsByTagName or (similar based on the type whether it is a class or a tag or id etc..) which means it does this operation on the element test. So in IE when you do that it probably internally resolves the fact that you are trying to perform the operation on an html element and not an xml element and it changes the document type for the underlying context(But i don't know why it changes the parent context though rather than just returning a match). You can check this out by this simple example as well.
var $test = document.createElement("test");
console.log("However, this <test> has an xml tag prepended: \n"
+ $test.outerHTML);
$test.getElementsByTagName("test");
console.log("Now, it does not: \n" + $test.outerHTML);
Demo
Update
Here is a documented way of defining the custom elements
The custom element type identifies a custom element interface and is a sequence of characters that must match the NCName production and contain a U+002D HYPHEN-MINUS character. The custom element type must not be one of the following values:
annotation-xml,
color-profile,
font-face,
font-face-src,
font-face-uri,
font-face-format,
font-face-name,
missing-glyph
So according to this had your tag name been somename-test ex:- custom-test IE recognizes it and it works as expected.
Demo

Parsing a string returned from ckeditor using javascript string methods

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.

Javascript Bookmarklet Unresponsive

Javascript newb here. Creating a bookmarklet to automate a simple task at work. Mostly a learning exercise. It will scan a transcript on CNN.com, for instance: (http://transcripts.cnn.com/TRANSCRIPTS/1302/28/acd.01.html). It will grab the lead stories at the top of the page, the name and title of the guests on the show, and format them so that they can be copy pasted into another document.
I've come up with a simple version that includes some jQuery that grabs the subheading and then uses a regular expression to find the names of the guests (it will also exclude everything between (begin videoclip) and (end videoclip), but I haven't gotten that far yet. It then alerts them (will eventually print them in a pop-up window, alert is just for troubleshooting purposes).
I'm using http://benalman.com/code/test/jquery-run-code-bookmarklet/ to create the bookmarklet. My problem is that once the bookmarklet is created it is completely unresponsive. Click on it and nothing happens. I've tried minimizing the code first with no result. My guess is that cnn.com's javascript is conflicting with mine but I'm not sure how to get around that. Or do I need to include some code to load and store the text on the current page? Here's the code (I've included comments, but I took these out when I used the bookmarklet generator.) Thanks for any help!
//Grabs the subheading
var leadStories=$(".cnnTransSubHead").text();
//Scans the webpage for guest name and title. Includes a regular expression to find any
//string that starts with a capital letter, includes a comma, and ends in a colon.
var scanForGuests=/[A-Z ].+,[A-Z0-9 ].+:/g;
//Joins the array created by scanForGuests with a semicolon instead of a comma
var guests=scanForGuests.join(‘; ‘);
//Creates an alert in the proper format including stories and guests.
alert(“Lead Stories: “ + leadStories + “. ” + guests + “. SEE TRANSCRIPT FIELD FOR FULL TRANSCRIPT.“)
Go to the page. Open up developer tools (ctrl+shift+j in chrome) and paste your code in the console to see what's wrong.
The $ in var leadStories = $(".cnnTransSubHead").text(); is from jQuery and the link provided does not have jQuery loaded into the page.
On any modern browser you should be able to achieve the same results without jQuery:
var leadStories = document.getElementsByClassName('cnnTransSubHead')
.map(function(el) { return el.innerText } );
next we have:
var scanForGuests=/[A-Z ].+,[A-Z0-9 ].+:/g;
var guests=scanForGuests.join('; ');
scanForGuests IS a regular expression, you never actually matched it to anything - so .join() is going to throw an error. I'm not exactly sure what you're trying to do. Are you trying to scan the full text of the page for that regex? In that case something like this would be your best bet
document.body.innerText.match(scanForGuests);
keep in mind that while innerText removes html markup, it's far from perfect and what pops up in it is very much at the mercy of how the page's html is structured. That said, on my quick test it seems to work.
Finally, for something like this you should use an immediately invoked function or you're sticking all your variables into the global context.
So putting it all together you get something like this:
(function() {
var leadStories = document.getElementsByClassName('cnnTransSubHead')
.map(function(el) { return el.innerText } );
var scanForGuests=/[A-Z ].+,[A-Z0-9 ].+:/g;
var guests = document.body.innerText.match(scanForGuests).join("; ");
alert("Leads: " + leadStories + " Guests: " + guests);
})();

Categories