How to wait for the text of table row to change? - ruby

I have the HTML below:
<tbody>
<tr>
<td>
<span>
<span> I Will Change </span>
</span>
I am trying to figure out when the text inside of the span changes and store the new text in a variable. This is what I tried:
waitForClick = wait.until {
newText = ExpectedConditions.invisibilityOfElementWithText(xpath, //path for the span with the text
}
This produces a syntax error and I am not sure how to use the invisibilityOfelementswithText. How could I use this in Ruby and what is the best way to notice a change of text? I do not know what the text will change to so I cannot have a wait.until either

I'm not ruby developer but i think that the idea needs to be something like:
while(driver.PageSource.Equals("I Will Change"))
{
sleep(1000)
}

I don't think there's a way to do this using ExpectedConditions. The text related methods are all related to finding text not for waiting for text to change. In Java there is FluentWait but I can't find any reference to it for Ruby. FluentWait allows you to write a custom wait so that you could wait for the current element with the current text to return false which would signal a text change. Other than that, you would have to implement it yourself. I don't know Ruby but the pseudocode would be like
timeout = 20 // seconds
while (element.text == "I Will Change" and timeElapsed < timeout)
{
sleep(100)
}
Make sure you add the timeout to the code above if you implement it yourself. You don't want the expected text to be slightly off and the script get stuck and never finish.
As to the InvisibilityOfElementWithText() method, it's looking for an element to go invisible or to be removed. I don't think that's what you want here because you are expecting the element to persist but the text contained in it to change.

Related

How to retrieve hidden elements when visibility is hidden

I want to retrieve hidden text when the visibiility attribute is hidden:
<div id = "tt52433002" class="yui-module yui-overlay yui-tt yui-overlay-hidden" style="z-index: 2; visibility: hidden;">
<div class="bd">Associated with the domain : testci20160503105556.com</div>
</div>
I tried:
browser.hidden(:class, 'bd').text
and
browser.hidden(:class, 'bd').value
But I get this error:
"unable to locate element, using {:class=>"bd", :tag_name=>"input", :type=>"hidden"}"
Watir is designed to act like a user. So if a user can not see the text in an element, then Watir will not return the text of the element.
Also, the element you are looking for is a div not a hidden.
If you need the text you can do:
browser.div(class: 'bd').inner_html
which makes a JavaScript call to provide the result.
This works:
browser.div.attribute_value('id') => tt52433002
as does this:
browser.div(class: 'bd').inner_html[/testci\d{14}/] => testci20160503105556
First things first. The error says that Watir cannot find an element using the criteria you specified. That means either that no such thing exists anywhere in the DOM, or that it might be inside a frame.
Since the element you want is a div, then you should be using the .div method to access it
browser.div(:class => 'bd') #finds first matching div
A potential second problem could occur if that classname is not very unique. Unless you specify an additional parameter, such as index, or perhaps a portion of the text contained by the div, you may not find the div you are looking for. A fast debugging trick (I like to do it from IRB) is to get a collection of matching divs and check the size
div_count = browser.divs(:class => 'bd').size
puts "there are #{divcount} divs of class bd in the dom"
If the count is anything more than 1, then you likely need to change how you are selecting it to ensure you get the right one. for example
browser.div(:class => 'bd', :text => /Associated with the domain/)
If the count is zero, then check for frames
frame_count = browser.frames.size
iframe_count = browser.iframes.size
If there are frames you will have to tell watir to look inside the frame for the div, if more than one frame then be sure you specify the right one
browser.frame.div(:class => 'bd') #looks for div inside first frame
Once you are sure you have the right div, then you ought to be able to use a method like .text or as in another answer .inner_html to get the contents of the div.

Check programmatically if a value is compliant with a datavalidation rule

I am using data validation rules on a Google Spreadsheet.
In my scenario, I need users to entry only valid values. I use the 'Reject input' to force them to write only validated content.
However, the 'Reject input' option works for manually entried data only, but it does not work if the user pastes content into the cell from a different source (e.g. a MS Excel document). In that case, a warning is still shown but the invalid value is written.
In other words, I need the 'Reject input' option to work also with pasted content.
OR... another approach would be to programmatically check the validity of the value according the Datavalidation rule for that cell.
Any ideas?
Thank you in advance.
I had a little play with this.
I had inconsistent behavior from google.
On occasion when I ctrl-c and ctrl-p, the target cell lost its data validation!
To do this programmatically
Write myfunction(e)
Set it to run when the spreadsheet is edited, do this by Resources>Current Project's Triggers
Query e to see what has happened.
Use the following to gather parameters
var sheet = e.source.getActiveSheet();
var sheetname = sheet.getSheetName();
var a_range = sheet.getActiveRange();
var activecell = e.source.getActiveCell();
var col = activecell.getColumn();
var row = activecell.getRow();
You may wish to check a_range to make sure they have not copied and pasted multiple cells.
Find out if the edit happened in an area that you have data validated;
if (sheetname == "mySheet") {
// checking they have not just cleared the cell
if (col == # && activecell.getValue() != "") {
THIS IS WHERE YOU CHECK THE activecell.getValue() AGAINST YOUR DATA VALIDATION
AND USE
activecell.setValue("") ;
to clear the cell if you want to reject the value
}
}
The obvious problem with this is that it is essentially repeating programmatically what the data validation should already be doing.
So you have to keep two sets of validation rules synchronized. You could just delete the in sheet data validation but I find that useful for providing the user feedback. Also is the data validation you are using provides content it is practical to leave it in place.
It would be great if there was a way of detecting that ctrl-p had been used or one of the paste-special options and only run the script in those cases. I would really like to know the answer to that. Can't find anything to help you there.
Note also that if someone inserts a row, this will not trigger any data validation and the onEdit() trigger will not detect it. It only works when the sheet is edited and by this I think it means there is a content change.
onChange() should detect insertion, it is described as;
Specifies a trigger that will fire when the spreadsheet's content or
structure is changed.
I am posting another answer because this is a programmatic solution.
It has a lot of problems and is pretty slow but I am demonstrating the process not the efficiency of the code.
It is slow. It will be possible to make this run faster.
It assumes that a single cell is pasted.
It does not cater for inserting of rows or columns.
This is what I noticed
The onEdit(event) has certain properties that are accessible. I could not be sure I got a full listing and one would be appreciated. Look at the Spreadsheet Edit Events section here.
The property of interest is "e.value".
I noticed that if you typed into a cell e.value = "value types" but if you pasted or Paste->special then e.value = undefined. This is also true for if you delete a cell content, I am not sure of other cases.
This is a solution
Here is a spreadsheet and script that detects if the user has typed, pasted or deleted into a specific cell. It also detects a user select from Data validation.
Type, paste or delete into the gold cell C3 or select the dropdown green cell C4.
You will need to request access, if you can't wait just copy & paste the code, set a trigger and play with it.
Example
Code
Set the trigger onEdit() to call this or rename it to onEdit(event)
You can attach it to a blank sheet and it will write to cells(5,3) and (6,3).
function detectPaste(event) {
var sheet = event.source.getActiveSheet();
var input_type =" ";
if (event.value == undefined) { // paste has occured
var activecell = event.source.getActiveCell();
if (activecell.getValue() == "") { // either delete or paste of empty cell
sheet.getRange(5,3).setValue("What a good User you are!");
sheet.getRange(6,3).setValue(event.value);
input_type = "delete"
}
else {
activecell.setValue("");
sheet.getRange(5,3).setValue("You pasted so I removed it");
sheet.getRange(6,3).setValue(event.value);
input_type = "paste";
}
}
else { // valid input
sheet.getRange(5,3).setValue("What a good User you are!");
sheet.getRange(6,3).setValue(event.value);
input_type = "type or select";
}
SpreadsheetApp.getActiveSpreadsheet().toast('Input type = ' + input_type, 'User Input detected ', 3);
}

Iterating a JQuery UI Datepicker via CasperJS

I want to use CasperJS to click each enabled date in a JQuery UI datepicker, do something while that is clicked, and then go back and click the next date. The problem here is that once I have the datepicker displayed, I don't know how to get the selectors for the anchors within its body and its body only.
I don't think that CasperJS does anything similar to jquery where you can pass it a selector and get an array of elements back.
I could drop into an evaluate call and get the array via jquery, but then I'd have to send it back as valid unique selectors for CasperJS to loop over.
I could use jquery to get the content of each anchor, like "1", and then use CasperJS's clickLabel, but clickLabel scopes only by tag, so all I could say is "click the anchor whose content is '1'", and then potentially click the wrong thing if there are multiple anchors with the content "1" onscreen.
You don't need jQuery to do this. XPath can be used to devise a good selector function for the datepicker days like so:
var x = require('casper').selectXPath;
function daySelector(day) {
return x("//*[contains(#class,'ui-datepicker')]//td[#data-handler='selectDay']/a[text()='"+day+"']");
}
Since you know the numbers that you want to click, you can do (untested):
var numbers = [2,3,4,5,6,7],
pickerSelector = ".ui-datepicker";
casper.thenEach(numbers, function(num){
num = num.data;
// TODO: click into the date field
this.waitUntilVisible(pickerSelector);
this.thenClick(daySelector(num));
this.waitWhileVisible(pickerSelector);
// TODO: Check or do something you need
});

Ruby/Selenium WebDriver 2: Find New Elements Fast

Environment:
Ruby 1.8.7
Selenium WebDriver 2
Ubuntu 12.04 Desktop
Firefox 13.0.1
Problem:
I have div tags being used as selects. You click on the div and a dropdown style window shows up with more div tags as the select options. Now I can go in and create a way of clicking on each of these options for each 'div as select' but what I'd really like is to write a piece of code that clicks on the divs I know about and then determines which divs are now displayed and enabled after the click. Then I can click on 1 of the options at random.
So my current code is something like this:
allDivs = brwsr.find_elements(:tag_name, 'div')
origDivs = allDivs
allDivs.each do |e|
if ... # Get the div I want
e.click
newDivs = brwsr.find_elements(:tag_name, 'div')
origDivs.each do |orig|
newDivs.delete(orig)
end
# Do something with remaining new divs here
end
end
The main problem I have is that this is extremely slow. I currently have around 200 divs to spin thru and this method takes a few minutes to complete. Normally, I wouldn't expect spinning thru a couple of hundred array elements to take very long. But it does.
To shorten the time I've attempted filtering the allDivs and newDivs by spinning thru them once and deleting anything that is not displayed and enabled.
So I'm currently stuck with a really slow solution. I don't mind this 'finding of new divs' being a little slower as it should also lead to much shorter script dev times. However, taking minutes to complete is way too long.
First question: Can Selenium return only elements which as displayed and enabled with some extra API string around the find_elements call?
Second question: Does someone have a better way of finding the new div elements without having to go thru the array of elements.
It would be very helpful if you could post some sample HTML of the DOM you are trying to automate.
With that said, the ruby webdriver bindings do support finding elements by chaining. Thus, given some html like this:
<div class="outer">
<div class="one">
<div class="alpha">A</div>
<div class="beta">B</div>
<div class="gamma">C</div>
</div>
<div class="two">
<div class="alpha">A</div>
<div class="beta">B</div>
<div class="gamma">C</div>
</div>
<div class="three">
<div class="alpha">A</div>
<div class="beta">B</div>
<div class="gamma">C</div>
</div>
</div>
You could write something like this to find the "B" div in the second group of divs ("two"):
group = brwsr.find_element(:class => "two")
desired_item = group.find_element(:class => "beta")
Or, even more simply through ruby's chaining capabilities:
desired_item = brwsr.find_element(:class => "two").find_element(:class => "beta")
One other way, which I prefer the most is to select an element via a CSS selector, which selects the element faster with an easy to read syntax:
desired_item = brwsr.find_element(:css => "div.two div.beta")

Need to click a bid button with Grease monkey script

I'm a bit stuck. I'm trying to create an Grease Monkey script that will automatically click an pop-up that appears on an auction site. I'v got the Xpat, but i'm too in expierienced with GM to get it to work.
Here is theelement inspection line i get from fire finder for firebug:
<input type="submit" style="width: 160px;" class="simplemodal-close" id="ctl00_mainContentPlaceholder_Button3" onclick="closePopup(); return false;" value="Back To Auctions" name="ctl00$mainContentPlaceholder$Button3">
and the firpath, xpath line is:
.//*[#id='ctl00_mainContentPlaceholder_Button3']
xpather line for full xpath:
/html/body/form[#id='aspnetForm']/div[#id='simplemodal-container']/div/div[#id='basic-modal-content']/div[#id='modal_winningBanner']/div/div[2]/div[2]/input[#id='ctl00_mainContentPlaceholder_Button3']
So what i used in my gm script to try to get it to click the button is as follows:
// #include *
// #version 0.1
// #description Automatically click // ==/UserScript==
click_popupBtn1 = function() {
var joinBtn=document.evaluate('//*[#id, "ctl00_mainContentPlaceholder_Button3"]'
,document,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null).singleNodeValue.click();
alert(joinBtn);
if(!joinBtn) return false;
joinBtn.click();
return true;
}
click_popupBtn1 ();
I think ive got something wrong on the syntax, but dont know how to debug GM. I've only worked with turbo pascal a few years ago, but would like to get some simple things done in java and GM.
Any help would be apreciated.
Thanks
Ludwig
umm, I don't understand a lot of the words you used or this complex syntax.
but something like:
document.getElementById("ctl00_mainContentPlaceholder_Button3").click();
should work.
var joinBtn=document.evaluate('//*[#id, "ctl00_mainContentPlaceholder_Button3"]'
,document,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null).singleNodeValue.click();
The first argument above is not a syntactically legal XPath expression.
Should be:
//*[#id = "ctl00_mainContentPlaceholder_Button3"]

Resources