var links = new List<GeckoElement>();
foreach (var link in geckoWebBrowser1.Document.Links)
{
if (!String.IsNullOrEmpty(link.GetAttribute("href").ToString()))
links.Add(link);
}
}
I have this code to collect all links in the page, but I cant figure it out how can I filter to some specific links that starts with for example "ow.ly". Rest in the list should be ignored.
Tryed this but didnt seem to work
if (links.Count > 0)
{
if (links.Equals("ow.ly"))
{
}
}
When I debug if links equals ow.ly it shows 0 reults.
links is a a List<GeckoElement>. So it’s unlikely that the list equals to the string "ow.ly". Instead, you want to filter the list for items, which href property contains that text.
You could do that for example like this:
var owlyLinks = geckoWebBrowser1.Document.Links.Where(link =>
{
string hrefAttribute = link.GetAttribute("href").ToString();
return !string.IsNullOrEmpty(hrefAttribute) && hrefAttribute.Contains("ow.ly");
}).ToList();
You might want to adjust the check so that "ow.ly" needs to appear somewhere special instead of just somewhere inside the string. For example, you could parse the whole Url, and then check that the host name equals to ow.ly.
Related
I'm trying to spy a web site with Selenium and C# to watch if new items appears on it.
Inside this web site, I have a list of <div class="myClass"> elements displayed like a table with many sub divs.
Periodically, I read all displayed divs and check if a new one is present.
But the number of divs is limited and when a new element appears, the oldest one is removed and it making an issue on my code.
Example: The max number of items is 3.
I get 3 items A,B,C.
a new item D appears.
I read the 3 displayed items: when I'm reading all sub div of D, a new item E comes and D is moved and I get an error like "Element is no longer attached to the DOM"
I get this error because the reading of all D sub divs is too long.
I'm getting the webelement using FindElement(By.Name("elemName")
There is a way to keep the element in memory even if this one is move or removed from the DOM?
Thanks!
This is an interesting problem that I've run into in the past. Through a few frustrating debugging sessions, I found that my WebElement objects were dynamically changing based on what was visible on the page. I would retrieve a List<WebElement>, change something on the page, and notice that my List<> would actually change with the contents on the page.
To answer your question.....I'm not totally sure if this is possible, to store the WebElement itself, without removing the old div as you mentioned.
The only workaround I would suggest to this is creating your own Element class and assigning WebElements to this class so you can basically create your own "cache" of elements.
Here's a little sample:
public class Element
{
public string TagName { get; set; }
public string Text { get; set; }
public bool Displayed { get; set; }
// any other attributes you want to store here
}
public List<Element> StoreWebElements(List<IWebElement> seleniumElements)
{
var result = new List<Element>();
foreach (var elem in seleniumElements)
{
result.Add(new Element { TagName = elem.TagName,
Text = elem.Text,
Displayed = elem.Displayed
};
}
}
// your code
var divList = Driver.FindElements(By.Name("elemName"));
// "store" the elements
var cachedElements = StoreWebElements(divList);
// do something on the page to change it
// divList will change, but cachedElements will stay the same.
It's a bit of a hacky solution, but I personally never found a workaround to the issue that you have described. I would be curious to see if anyone else has an explanation or better solution for this.
I try to explain my problem:
Okay, I need the KSCHL and the Info.
I need the KSCHL from the result file and then I want to search after the KSCHL in the other file "Data".
In the first file I have all KSCHL.
var kschlResultList = docResult.SelectNodes(...);
var kschlDataList = docData.SelectNodes(...);
var infoDataList = docData.SelectNodes(...);
for (int i = 0; i < kschlResultList.Count; i++)
{
string kschlResult = kschlResultList[i].InnerText;
for (int x = 0; x < kschlDataList.Count; x++)
{
string kschlData = kschlDataList[x].InnerText;
if (kschlData == kschlResult)
{
for (int y = 0; y < infoDataList.Count; y++)
{
string infoData = infoDataList[y].InnerText;
if (infoData == kschlResult)
{
//I know the If is false
string infoFromKschl = infoData;
}
}
}
}
}
The problem is now to find the KSCHL (from the first file) in the second file and then to search after the "info".
So if I have the KSCHL "KVZ1" in the first file, then I want to search this KSCHL in the second file and the associated Info for it.
Hope you understand :)
You don't have to loop quite so much. :-)
Using XPath - the special strings inside SelectNodes() or SelectSingleNode(), you can go pretty directly to what you want.
You can see a great basic example - several really - of how to select an XML node based on another node at the same level here:
How to select a node using XPath if sibling node has a specific value?
In your case, we can get to a list of the INFO values more simply by looping just through the KSCHL values. I use them as text, because I want to make a new XPath string with them.
I'm not clear exactly what format you want the results in, so I'm simply pushing them into a SortedDictionary for now.
At that last step, you could do other things as is most useful to you..... such as push them into a database, dump them in a file, send them to another function.
/***************************************************************
*I'm not sure how you want to use the results still,
* so I'll just stick them in a Dictionary for this example.
* ***********************************************************/
SortedDictionary<string, string> objLookupResults = new SortedDictionary<string, string>();
// --- note how I added /text()... doesn't change much, but being specific <<<<<<
var kschlResultList = docresult.SelectNodes("//root/CalculationLogCompact/CalculationLogRowCompact/KSCHL/text()");
foreach (System.Xml.XmlText objNextTextNode in kschlResultList) {
// get the actual text from the XML text node
string strNextKSCHL = objNextTextNode.InnerText;
// use it to make the XPath to get the INFO --- see the [KSCHL/text()= ...
string strNextXPath = "//SNW5_Pricing_JKV-Q10_full/PricingProcedure[KSCHL/text()=\"" + strNextKSCHL + "\" and PRICE>0]/INFO/text()";
// and get that INFO text! I use SelectSingleNode here, assuming only one INFO for each KSCHL..... if there can be more than one INFO for each KSCHL, then we'd need another loop here
string strNextINFO = docdata.SelectSingleNode(strNextXPath)?.InnerText; // <<< note I added the ? because now there may be no result with the rule PRICE>0.
// --- then you need to put this result somewhere useful to you.
// I'm not sure what that is, so I'll stick it in the Dictionary object.
if (strNextINFO != null) {
objLookupResults.Add(strNextKSCHL, strNextINFO);
}
}
had a look around and found many similar questions but none matching mine exactly.
public bool checkInvalid()
{
invalidMessage = filterWords.Any(s => appmessage.Contains(s));
return invalidMessage;
}
If a string is found that matches a string in the list the boolean invalidMessage is set to true.
After this though I would like to be able to add each string found to a list. is there a way I can do this using .Contains() or can someone recommend me another way to go about this?
Many thanks.
Well, from your description, I thought here is what you want:
// Set of filtered words
string[] filterWords = {"AAA", "BBB", "EEE"};
// The app message
string appMessage = "AAA CCC BBB DDD";
// The list contains filtered words from the app message
List<string> result = new List<string>();
// Normally, here is what you do
// 1. With each word in the filtered words set
foreach (string word in filterWords)
{
// Check if it exists in the app message
if (appMessage.Contains(word))
{
// If it does, add to the list
result.Add(word);
}
}
But as you said, you want to use LINQ, so instead of doing a loop, you can do it like this:
// If you want to use LINQ, here is the way
result.AddRange(filterWords.Where(word => appMessage.Contains(word)));
If what you want is to gets the words in filterWords that are contained in appmessage you can use Where:
var words = filterWords.Where(s => appmessage.Contains(s)).ToList();
I am using v2.52 of Selenium and the WebDriver with C#. What I am trying to achieve should be fairly simple, but I am unable to find the solution for: I'd like to find an element by multiple By-criteria.
Let's say, I have something like this:
Click me!
Click me!
<p class="foo">Click me!</p>
Ignore the fact, that I could use By.CssSelector, By.CssSelector, By.XPath and so on. I was assuming that it should be possible to do something like this:
driver.FindElement(By.TagName("a").ClassName("foo"))
// ...or something like this...
driver.FindElement(By.TagName("a"), By.ClassName("foo"))
OpenQA.Selenium.Support.PageObjects.ByChained does not do the trick, since it is searching hierarchical.
Is there a way to find element(s) which match multiple By-criteria?
Best regards,
Carsten
I imagine something like this may work for your case:
public IWebElement FindElementByMultipleCriteria(List<By> Criteria, IReadOnlyCollection<IWebElement> toFilter = null)
{
// If we've reached the end of the criteria list, return the first element:
if (Criteria.Count == 0 && toFilter != null) return toFilter.ElementAt(0);
// Take the head of the criteria list
By currentCriteria = Criteria[0];
Criteria.RemoveAt(0);
// If no list of elements found exists, we get all elements from the current criteria:
if (toFilter == null)
{
toFilter = Driver.FindElements(currentCriteria);
}
// If a list does exist, we must filter out the ones that aren't found by the current criteria:
else
{
List<IWebElement> newFilter = new List<IWebElement>();
foreach(IWebElement e in Driver.FindElements(currentCriteria))
{
if (toFilter.Contains(e)) newFilter.Add(e);
}
toFilter = newFilter.AsReadOnly();
}
// Pass in the refined criteria and list of elements found.
return FindElementByMultipleCriteria(Criteria, toFilter);
}
IWebElement example = FindElementByMultipleCriteria(new List<By>(){ By.TagName("a"), ClassName("foo") });
Essentially, you're taking the list of elements found by the first By passed in. You are then going through the remaining By criteria and removing elements from that initial list that are never found again.
This is horribly inefficient and I don't really understand why you would want to do this, but it exists.
Oh also, you'll need to add using System.Linq; in order to interact with the IReadOnlyCollection.
Well I think you can try something like this, and let me know if it works:
public IWebElement FindElementUsingNestedBy(By firstCriteria, By secondCriteria)
{
var allElements = Driver.FindElements(firstCriteria);
foreach (var webElement in allElements)
{
try
{
var desiredObject = webElement.FindElement(secondCriteria);
return desiredObject;
}
catch (NotFoundException ex)
{
}
}
return null;
}
I have the below code to get alexa page rank.
private int GetAlexaRank(string domain)
{
var alexaRank = 0;
try
{
var url = string.Format("http://data.alexa.com/data?cli=10&dat=snbamz&url={0}", domain);
var doc = XDocument.Load(url);
var rank = doc.Descendants("POPULARITY").Select(node => node.Attribute("TEXT").Value).FirstOrDefault();
if (!int.TryParse(rank, out alexaRank))
alexaRank = -1;
}
catch (Exception e)
{
return -1;
}
return alexaRank;
}
But I truly don't understand how does that code work???
Would you tell me exactly, what is the "POPULARITY" and "TEXT"? and where are they stored?
I don't understand this syntax: doc.Descendants("POPULARITY").Select(node => node.Attribute("TEXT").Value).FirstOrDefault();
Please!!!!
I would recommend you navigating to the url you have in your code and take a look at the XML file structure. You should see tags with POPULARITY and TEXT, by the look of your code these are the nodes/attributes/values that you are selecting.
.Descendants returns a collection, and since you provide POPULARITY it will bring back the elements within the <POPULARITY> tag in your XML file.
You are then looking at each node in the Collection whose Descendants are Popularity and selecting an item with attribute TEXT. Your return the first value found, or null if the collection does not contain a node with attribute TEXT.