So, working in C#. I have a CSS-based drop-down menu. I open the menu, then grab a reference to it using FindElement by CSSSelector. I then grab the contents of the list using FindElements, again by CSSSelector.
Now here's where it get's interesting. I iterate the list, based on a file I have open in a streamreader.
Looks something like:
list = driver.FindElement(By.CSSSelector("dropdown-menu"));
list_items = list.FindElements(By.CSSSelector("LI > A"));
int row = 0;
while (data_file.read())
{
iWebElement item = list_items[row];
string label = item.text;
string url = item.getattribute("href");
assert.areequal("something", label);
assert.areequal("something else", url);
row++;
}
Now here's the thing: if the mouse pointer is placed over the drop-down, while this is executing, item.text returns value and the test succeeds. If the pointer is anywhere else, item.text will be blank and the test fails. Trying to understand what's going on, and taking a clue from the fact that though the test would fail when running, but would succeed while stepping, I modified the code with a loop:
while (data_file.read())
{
iWebElement item = list_items[row];
string label = item.text;
while (label == "")
{
label = item.text;
}
string url = item.getattribute("href");
assert.areequal("something", label);
assert.areequal("something else", url);
row++;
}
Now the test will always succeed, but if the pointer is not on the control it is SIGNIFICANTLY slower... we're talking a factor of 4 or 5... then when the pointer IS on the control. By wrapping a timer around this, I find that it typically takes between 2 and 4 seconds before .text returns anything but an empty string... sometimes longer.
Again, this delay only seems to apply when the mouse pointer is not over the drop-down. Otherwise, the value appears to be there instantaneously.
Can anyone suggest a possible explanation for why it's behaving this way, and a possible approach to solving it?
BTW, I'm not finding any difference between:
item = list_items[row];
label = item.text;
and
label = list_items[row].text;
Nor does .getattribute("value") produce any faster results than .text.
As for why the menus are acting this way, it's hard to tell with the code you've provided. If you could provide the code that displays your menu, that might help. As for solutions, there are a couple.
The label could be taking longer to display because of the while loop itself - it's just going crazy grabbing the text over and over very quickly. A better solution would be to wait for the element to be present. This may make your code run faster. See the Selenium Website for information on using WebDriverWait.
Alternatively, there is an "ugly" solution. You can just move the mouse to the menu with Selenium, to make sure the menu is always displayed when you need it. I've adapted some code from here as an example:
OpenQA.Selenium.Interactions.Actions builder = new
builder.MoveToElement(list).Build().Perform();
Hope this helps!
Related
I'm currently implementing a Custom Spell Check in WPF using NHunspell, because the native solution of .Net Framework doesn't fit my needs. But I'm having trouble when checking the words in a big text, such as a Lorem Ipsum with 10 pargraphs, because i need to check each word, see if it contains in the dictionary the Hunspell uses, and if not, I need to Underline that Word.
I have this current method, that checks all the text everytime the KeyUp is a Backspace or a Space Key.
var textRange = new TextRange(SpellCheckRichTextBox.Document.ContentStart,
SpellCheckRichTextBox.Document.ContentEnd);
textRange.ApplyPropertyValue(Inline.TextDecorationsProperty, null);
_viewModel.Text = textRange.Text;
var zzz = _viewModel.Text.Split(' ');
var kfeofe = zzz.Where(x => _viewModel.MisspelledWords.Contains(x));
foreach (var item in kfeofe)
{
TextPointer current = textRange.Start.GetInsertionPosition(LogicalDirection.Forward);
while (current != null)
{
string textInRun = current.GetTextInRun(LogicalDirection.Forward);
if (!string.IsNullOrWhiteSpace(textInRun))
{
int index = textInRun.IndexOf(item.ToString());
if (index != -1)
{
TextPointer selectionStart = current.GetPositionAtOffset(index, LogicalDirection.Forward);
TextPointer selectionEnd = selectionStart.GetPositionAtOffset(item.ToString().Length, LogicalDirection.Forward);
TextRange selection = new TextRange(selectionStart, selectionEnd);
selection.ApplyPropertyValue(Inline.TextDecorationsProperty, TextDecorations.Underline);
}
}
current = current.GetNextContextPosition(LogicalDirection.Forward);
}
}
But, I think I need a Async solution, so it doesn't block my main thread and the typing of the user.
- In theory I was thinking about running a parallel thread if the user spends more than 2 seconds without typing and then returning the checked TextRange to my RichTextBox (SpellCheckRichTextBox).
Can somebody suggest any solution so I can make the verification less slow when working with big texts? I'm really stuck at that, any help would be appreciated.
Thanks in advance!
The first improvement would be
zzz.AsParallel().Where(x => _viewModel.MisspelledWords.Contains(x)).ToList();
That obviously assumes that your .MisspelledWords.Contains(x) is something that can be done in parallel. It might be a ConcurrentDictionary already.
The fact that you have a collection of misspelled words, makes me believe you already parsed the text once. So why parse it twice? Why can't you combine those two passes? That would be another possible optimization.
And yes, doing all of this in another thread when the user stops typing would be preferable.
I'm almost finished writing a small add-on for League of Legends that allows players to click a checkbox in my form, and the checkbox's function clicks in the search bar (which automatically clears it. Already a feature in the LoL Client) and types out the names of all the champions the player has assigned to that group via a 2nd form. However, if the text is (example) 12345, I've been getting a lot of results like this: 1231234545.
What I've tried:
I've tried adding in ctrl-a + backspace with sendkeys because maybe when it clicks in the search bar it isn't always deleting the stuff there! This however did not change anything
I then commented out the code that types out the actual string (12345) and made the checkbox ONLY type ctrl-a. I then wrote out a string longer than the search bar that I'm typing into and pasted it, clicked a checkbox, and tried to see if it would highlight the text (meaning the text was not always being cleared when clicked). I did this about 50 times and every single time the text was cleared
This leads me to believe that the problem is in the code itself, not the actual integration with the LoL Client. So maybe it's a problem with StringBuilder? I haven't used it very often so I'm not too familiar with it. Commented out all the stringbuilder stuff and changed to String concatenation. Same problem
I tried adding a boolean runningCheckboxFunction to make sure the function would only run once but still have the issue as well.
So this is where I'm at right now. I'm pretty sure it's a problem with my code looping for some reason or the checkbox function as a whole being called twice instead of once when I click the checkbox.
My code:
private void checkboxFunction()
{
if (runningCheckboxFunction == true) return;
runningCheckboxFunction = true;
//CLICK IN THE SEARCH BAR (Automatically clears the text that WAS there)
Process[] LolClient = Process.GetProcessesByName("LolClient"); //should only return one client (i.e. get [0])
if (LolClient.Length < 1) return;
ClickOnPoint(LolClient[0].MainWindowHandle, searchBarCheckPoint);
//MAKE THE STRING TO TYPE IN THE SEARCH BAR
StringBuilder whatToTypeInSearchBar = new StringBuilder();
foreach (CheckBox cb in checkboxes)
{
if (cb.Visible == true && cb.Checked == true)
{
List<String> thisCheckboxsChamps = settingsForm.getChampionListForGroup(checkboxes.IndexOf(cb));
if (thisCheckboxsChamps == null) continue;
foreach (String champ in thisCheckboxsChamps)
{
whatToTypeInSearchBar.Append("|");
whatToTypeInSearchBar.Append(champ);
whatToTypeInSearchBar.Append("$");
}
}
}
//had an issue that sometimes clicking in search bar did not automatically clear it, so select all text and backspace just in case
//^ = ctrl -- ^A = ctrl-A (select all)
SendKeys.Send("^A");
//backspace
SendKeys.Send("{BACKSPACE}");
//TYPE IN THE SEARCH BAR
if (whatToTypeInSearchBar.Length > 0)
{
//remove the first "|"
whatToTypeInSearchBar.Remove(0, 1);
SendKeys.Send(whatToTypeInSearchBar.ToString());
}
runningCheckboxFunction = false;
}
Which event are you using for the check box control (CheckedChanged or CheckStateChanged)? Also, what is the value for CheckBox.ThreeState property? It is possible that the event you use fires more than once. If that is the case, you could check for a specific state before calling the function you have listed above.
Also, when you use SendKeys, try sending the keys to your own application and monitor the ActiveControl.KeyUp/ActiveControl.KeyDown/ActiveControl.KeyPress events to ensure that the string you generate is the riight sequence.
I'm new to C# and i'm doing this just for practice (this isn't homework)
Okay so I need to convert a text box text (called Numbers) into an integer
I tried something like:
int number1;
number1 = int.Parse(Numbers.Text);
Then to check if it's right:
label1.Text = number1.ToString();
MessageBox.Show(number1.ToString());
But integer doesn't hold anything. I get no Message and the label doesn't change.
Additional question:
Why doesn't the message box doesn't show?
There were no if statements either switches.
When it comes to user input and parsing, you may want to try Int32.TryParse. if gives you the ability to parse, but also that secondary feedback letting you know if it was a success or not. For example:
Int32 parsed;
String input = "3";
if (Int32.TryParse(input, out parsed)){
// it was successful and `parsed` = 3
} else {
// `input` most likely had something invalid
}
I have verified your code, and what you have posted is fine.
I assume your problem is the code isn't being ran. Make sure the method is being called.
If you can't fix it still, add a new button on the form. Double click on the button, and add that code to the method that is automatically created.
Then test it by clicking on the button at runtime.
This is pretty basic stuff and should work. Considering that the MessageBox doesn't appear at all, I guess you need to clean your build. Either choose Clean from Solution's context menu, or close the solution and VS, go to project directory, remove bin and obj and come back and rebuild the project.
Here is a simple method
int i;
try
{
i=Convert.ToInt32(textBox1.Text);
}
catch
{
//do whatever you want
}
I have a large list of offsets which I need to highlight in my RichTextBox. However this process is taking too long. I am using the following code:
foreach (int offset in offsets)
{
richTextBox.Select(offset, searchString.Length);
richTextBox.SelectionBackColor = Color.Yellow;
}
Is there a more efficient way to do so?
UPDATE:
Tried using this method but it doesn't highlight anything:
richTextBox.SelectionBackColor = Color.Yellow;
foreach (int offset in offsets)
{
richTextBox.Select(offset, searchString.Length);
}
I've googled your issue and I found that RichTextBox is getting very slow when having many lines. In my opinion, you have either buy a third part control which you can be satisfied by its performance or you may need threads to devide the whole selection task. I think they can accelerate things up.
Hope it helps !
I've had this same problem before. I ended up disregarding all of the methods they give you and manipulated the underlying RTF data. Also, the reason that your second block of code doesnt work is that RTF applies formatting as it goes, so if you call a function (or Property in this case) to change the selection color, it will only apply it for the currently selected block. Any changes made to the selection after that call become irrelavent.
You can play around with the RGB values, or here is a great source on how to do different things within the RTF control. Pop this function in your code and see how well it works. I use it to provide realtime syntax highlighting for SQL code.
public void HighlightText(int offset, int length)
{
String sText = richTextBox.Text.Trim();
sText = sText.Insert(offset + length - 1, #" \highlight0");
sText = sText.Insert(offset, #" \highlight1");
String s = #"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Courier New;}}
{\colortbl ;\red255\green255\blue0;}\viewkind4\uc1\pard";
s += sText;
s += #"\par}";
richTextBox.Rtf = s;
}
Does it make any difference if you set the SelectionBackColor outside of the loop?
Looking into the RichTextBox with Reflector shows, that a WindowMessage is sent to the control every time when the color is set. In the case of large number of offsets this might lead to highlighting the already highlighted words again and again, leading to O(n^2) behavior.
I have a collection of records on a web page, and when a record is clicked, a 'Delete' link is displayed (actually 'unhidden' as its actually always there).
When trying to access this 'Delete' link, I am using its value.
When I use Driver.FindElement, it returns the first Delete link, even though it's hidden, and therefore can't click it (and shouldn't as it is not the right link).
So, what I basically want to do is find only non-hidden links. The code below works, but as it iterates through every Delete link I am afraid it may be inefficient.
Is there a better way?
public class DataPageModel : BasePageModel
{
private static readonly By DeleteSelector = By.CssSelector("input[value=\"Delete\"]");
private IWebElement DeleteElement
{
get
{
var elements = Driver.FindElements(DeleteSelector);
foreach (var element in elements.Where(e => e.Displayed))
{
return element;
}
Assert.Fail("Could not locate a visible Delete Element");
return null;
}
}
}
While I agree with #Torbjorn that you should be weary about where you spend your time optimizing, I do think this code is a bit inefficient.
Basically what is slowing the code down is the back and forth checking of each element to see if its displayed. To speed up the code, you need to get the element you want in one go.
Two options (both involve javascript):
jQuery
Take a look at the different ways to bring jQuery selectors to Selenium (I wrote about it here). Once you have that, you can make use of jQuery's :visible selector.
Alternatively if you know for sure the page already has jQuery loaded and you don't want to do all the extra code, you can simply use ExecuteScript:
IWebElement element = (IWebElement)driver.ExecuteScript("return $('input[value=\"Delete\"]:visible').first().get(0)");
Javascript
If you want to avoid jQuery you can just write a javascript function to do the same thing you are doing now in C#: Get all the possible elements and return the first visible one.
Then you would do something similar:
string script = //your javascript
IWebElement element = (IWebElement)driver.ExecuteScript(script);
You trade of readability with different degrees depending on which option you pick but they should all be more efficient. Of course these all require that javascript be enabled in the browser.