Capturing characters for fields which are enabled - java

The fields in question look like this
Each password character field is chosen randomly and the user is required to enter their corresponding password character in the specific field. For example if my password is swordfish, in the picture above I would be required to enter the letters s, d and s. So far I have an If statement for each element which is hardly ideal and only the input[] of the xpath changes with each field.
WebElement p1 = driver.findElement(By.xpath("/html/body/div[3]/div[3]
/div[2]/div/div[2]/div[1]/div[1]/div/div[2]/div[2]/div/form/table/tbody/tr[6]/td/input[1]
"));
if (p1.isEnabled()){
p1.click();
p1.sendKeys("s");
}
WebElement p2 = driver.findElement(By.xpath("/html/body/div[3]/div[3]
/div[2]/div/div[2]/div[1]/div[1]/div/div[2]/div[2]/div/form/table/tbody/tr[6]/td/input[2]
"));
if (p2.isEnabled()){
p2.sendKeys("d");
}
WebElement p3 = driver.findElement(By.xpath("/html/body/div[3]/div[3]
/div[2]/div/div[2]/div[1]/div[1]/div/div[2]/div[2]/div/form/table/tbody/tr[6]/td/input[3]
"));
if (p3.isEnabled()){
p3.sendKeys("s");
}
I have a solution in pseudocode but I don't know exactly how to do it in Java.
for i =0;i<10;i++
associate i with a field
if passworfield is enabled
set password letter based on xpath

If you take a look at the xPath you are using, they only differ on the index of the input.
For my suggested approach you would need to have a char array where you hold the password.
So, a possible approach would be to have a loop with an index more or less like this:
char[] password = new char[]{'p','a','s','s','w','o','r','d','1'};
for (int i = 0; i < 9; i++)
{
WebElement elem = driver.findElement(By.xpath("/html/body/div[3]/div[3]/div[2]/div/div[2]/div[1]/div[1]/div/div[2]/div[2]/div/form/table/tbody/tr[6]/td/input[" + (i + 1) + "]"));
if (elem != null && elem.isEnabled())
elem.sendKeys(Character.toString(password[i]));
}
Just notice that I'm using Character.toString because I'm not sure if sendKeys allows a char instead of a String. If there's any way that's possible I'd recommend you do so.
EDIT:
Another possibility that has been suggested is to get all the elements first (which would improve the performance as you'd not need to force Selenium to analyze the webpage over and over again.. where it would be simplified to (something approximately like):
char[] password = new char[]{'p','a','s','s','w','o','r','d','1'};
List<WebElement> elements = driver.findElements(By.xpath("/html/body/div[3]/div[3]/div[2]/div/div[2]/div[1]/div[1]/div/div[2]/div[2]/div/form/table/tbody/tr[6]/td/input"));
for (WebElement elem : elements)
{
if (elem.isEnabled())
elem.sendKeys(Character.toString(password[elements.indexOf(elem)]));
}

I'd go with Fernando's answer, but here's something that might help too.
String s1 = "0,p,a,s,s,w,o,r,d,1";
String[] array = s1.split(",");
for (int i = 1; i < 10; i++){
String xpath = "/html/body/div[3]/div[3]/div[2]/div/div[2]
/div[1]/div[1]/div/div[2]/div[2]/div/form/table/tbody/
tr[6]/td/input[" + i + "]";
WebElement passw = driver.findElement(By.xpath(xpath));
if (passw.isEnabled()){
passw.sendKeys(array[i]);
}
}

Related

Problem handling multiple substrings on dynamic string

Currently I am having a hard time trying to figure out if there is a better way to refactor the following code.
Given the following:
String detail = "POTATORANDOMFOOD";
Lets say I want to assign variables with different parts of detail, the end result would look something like this.
String title = detail.substring(0, 6); // POTATO
String label = detail.substring(6, 12); // RANDOM
String tag = detail.substring(12, 16); // FOOD
Now lets say the string detail length constantly changes, sometimes it only contains "POTATORANDOM" and no "FOOD", sometimes it contains even more characters "POTATORANDOMFOODTODAY", so another variable would be used.
String title = detail.substring(0, 6); // POTATO
String label = detail.substring(6, 12); // RANDOM
String tag = detail.substring(12, 16); // FOOD
...
String etc = detail.substring(30, 40); // etc value from detail string
The issue with this, is that since the string sometimes is shorter or longer, we would run into the StringIndexOutOfBoundsException which is not good.
So currently I have a naive way to handle this:
if (detail != null || !detail.isEmpty()) {
if (detail.length() >= 6) {
title = detail.substring(0, 6);
if (detail.length() >= 12) {
label = detail.substring(6, 12);
if (detail.length() >= 16) {
tag = detail.substring(12, 16);
.
.
.
}
}
}
}
This can get really messy, especially if lets say the string were to grow even more.
So my question is, what would be a good design pattern that would fit for this type of problem? I have tried the chain of responsibility design pattern but, the issue with this one is that it only returns a single value, while I am trying to return multiple ones if possible. This way I can assign multiple variables depending on the length of the string.
Any help/hints is greatly appreciated!
Edited:
The order and length are always the same. So title will always be first and it will always contain 6 characters. label will always be second and it will always contain 6 characters. tag will always be third and it will always contain 4 characters, etc.
If I was you, I would do the following:
Define a class to hold a Word definition
public class Word {
private final String name;
private final int startIndex;
private final int endIndex;
public Word(String name, int startIndex, int endIndex) {
this.name = name;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
public String getName() { return name; }
public int getStartIndex() { return startIndex; }
public int getEndIndex() { return endIndex; }
}
Create a static list which holds all the possible words
public static final List<Word> WORDS = List.of(
new Word("title", 0, 6),
new Word("label", 6, 12),
new Word("tag", 12, 16),
...
);
Create a function that parses the String detail by walking this list until when the size of the string is exhausted
... and of course storing the elements into a Map<String, String> so that you can access them later.
public Map<String, String> parseDetail(String detail) {
Map<String, String> receivedWords = new LinkedHashMap<>(); //<-- map respecting insertion order
if (detail.isEmpty()) {
return receivedWords;
}
int parsedLength = 0; ​
​for (Word word : WORDS) {
​receivedWords.put(word.getName(), detail.substring(word.getStartIndex(), word.getEndIndex()); //<-- store the current word
parsedLength += word.getEndIndex() - word.getStartIndex(); //increase the parsedLength by the length of your word
if (parsedLength >= detail.length()) {
break; //<-- exit the loop when you're done with the parsing
}
​}
return receivedWords;
}
To sum up:
Map<String, String> receivedWords = parseDetail(detail);
receivedWords.forEach((k, v) -> {
System.out.println("Key: " + k + ", value: " + v);
});
Output:
Key: title, value: POTATO
Key: label, value: RANDOM
Key: tag, value: FOOD
...
Tip 1: The input you receive looks pretty weird. I understand that you cannot change it but I would try to negotiate with the caller (if possible) a better way to send you their input (ideally a structured object, if not possible at least a string with some separator so that you can simply split by that character).
Tip 2: I have defined the list of words statically in the code. But I would instead define an external file (e.g. a Json file, or an Xml, or even a simple text file) that you parse dynamically to create the list. That will allow someone else to configure this file with the words/start index/end index without you having to do it in the code each time there is a change. ​
You could simply check the length of the total string to see if it has the RANDOM and the FOOD attributes before using substring()
String title = "", label = "", tag = "";
if (detail.length() >= 6)
title = detail.substring(0, 6);
if (detail.length() >= 12)
label = detail.substring(6, 12);
if (detail.length() == 16)
tag = detail.substring(12,16);
I would suggest a regex aproach:
public static void main(String[] args) {
String detail = "POTATORANDOMFOODTODAY";
Pattern p = Pattern.compile("(.{0,6})(.{0,6})(.{0,4})(.{0,5})");
Matcher m = p.matcher(detail);
m.find();
String title = m.group(1);
String label = m.group(2);
String tag = m.group(3);
String day = m.group(4);
System.out.println("title: " + title + ", lable: " + label + ", tag: " + tag + ", day: " + day);
}
//output: title: POTATO, lable: RANDOM, tag: FOOD, day: TODAY
If you have a lots of groups I would suggest to use named captured groups. The approach above can particularly be difficult to maintain as adding or removing a group in the middle of the regex upsets the previous numbering used via Matcher#group(int groupNumber). Using named capturing groups:
public static void main(String[] args) {
String detail = "POTATORANDOMFOODTODAY";
Pattern p = Pattern.compile("(?<title>.{0,6})(?<label>.{0,6})(?<tag>.{0,4})(?<day>.{0,5})");
Matcher m = p.matcher(detail);
m.find();
String title = m.group("title");
String label = m.group("label");
String tag = m.group("tag");
String day = m.group("day");
System.out.println("title: " + title + ", lable: " + label + ", tag: " + tag + ", day: " + day);
}
//output: title: POTATO, lable: RANDOM, tag: FOOD, day: TODAY
If the string is dynamic then it can essentially contain basically anything and since there can possibly be no whitespace(s) in the string the only way to know what a specific word (substring) might be is to play the string against a 'word list'. You can quickly come to realize how pivotal even a single whitespace (or separator character) can be within a string. Using the String#substring() method is only good if you already know what all the words within the detail string happen to be.
The simple solution would be to set acceptable rules as to how a specific string should be received. After all, why would you want to accept a string that contains multiple words without a separator character of some type to begin with. If the string has whitespaces in it, to separate the words contained within that string, a mere:
String[] words = string.split("\\s+");
line of code would do the trick. Bottom line, get rid of that nonsense of accepting strings containing multiple words with no separation mechanism included, even if that separation mechanism is by making use of the underscore ( _ ) character (or some other character). Well...if you can.
I suppose sometimes we just can't modify how we're dealt things (something like taxes) and how we receive specific strings is simply out of our control. If this is the case then one way to deal with this dilemma is to work against an established Word-List. This word list can in in the size of a few words to hundreds of thousands of words. The situation you need to deal with will determine the word list size. If small enough the word list can be contained within a String Array or a collection like an ArrayList or List Interface. If really large however then the word list would most likely be contained within a Text file. The word list I most commonly use contains well over 370,000 individual words.
Here is an example of using a small Word-List contained within a List Interface:
String detail = "POTATORANDOMFOODTODAY";
List<String> wordList = Arrays.asList(new String[] {
"pumpkin", "carrot", "potato", "tomato", "lettus", "radish", "bean",
"pea", "food", "random", "today", "yesterday", "tomorrow",
});
// See if the detail string 'contains' any word-list words...
List<String> found = new ArrayList<>();
for (int i = 0; i < wordList.size(); i++) {
String word = wordList.get(i);
if (detail.toLowerCase().contains(word.toLowerCase())) {
found.add(word.toUpperCase());
}
}
/* Ensure the words within the list are in proper order.
That is, the same order as they are received within the
detail String. This is necessary since words from the
word-List can be found anywhere within the detail string. */
int startIndex = 0;
List<String> foundWords = new ArrayList<>();
String tmpStrg = "";
while (!tmpStrg.equals(detail)) {
for (int i = 0; i < found.size(); i++) {
String word = found.get(i);
if (detail.indexOf(word) == startIndex) {
foundWords.add(word);
startIndex = startIndex + word.length();
String procStrg = foundWords.toString().replace(", ", "");
tmpStrg = procStrg.substring(1, procStrg.length() - 1);
}
}
}
//Format and Display the required data
if (foundWords.isEmpty()) {
System.err.println("Couldn't find any required words!");
return; // or whatever...
}
String title = foundWords.get(0);
String label = foundWords.size() > 1 ? foundWords.get(1) : "N/A";
String[] tag = new String[1];
if (foundWords.size() > 2) {
tag = new String[foundWords.size()-2];
for (int i = 0; i < foundWords.size() - 2; i++) {
tag[i] = foundWords.get(i + 2);
}
}
else {
tag[0] = "N/A";
}
System.out.println("Title:\t" + title);
System.out.println("Label:\t" + label);
System.out.println("Tags:\t"
+ Arrays.toString(tag).substring(1, Arrays.toString(tag).length() - 1));
When the above code is run the console window would display:
Title: POTATO
Label: RANDOM
Tags: FOOD, TODAY
You can use the Stream API and use filter() method.
Then you use map() to apply your existing logic, that should do the trick.
Switch-cases could be an alternative but it adds more LoC but reduces the arrow code of all the nested ifs

Algorithm for creating an id in java

After trying for a few hours now, I cant find a solution for my problem.
I want to generate an id with is generated like this:
I want my id to be generated from characters from a character array. For the following examples I will use the following array: [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,1,2,3,4,5,6,7,8,9,0].
I want my id to be generated so that my first 36 id's are the characters from the array and then I want the id to be two characters long and to go through all the possibilities.
It's kind of like a bruteforce algorithm somewhere.
If you guys have and ideas or questions please let me know.
Im still a programming noob!
(Note that i want the id to be generated by a method that returns a string)
Attempt:
public String generate() {
char[] s = new char[charset.length];
if (firstIndex == charset.length) {
firstIndex = 0;
secondIndex++;
index++;
}
if (secondIndex == charset.length) {
secondIndex = 0;
thirdIndex++;
}
if(index>charset.length){
index=charset.length;
}
for (int i = 0; i < index; i++) {
s[i] += charset[secondIndex];
}
s[thirdIndex] = charset[firstIndex];
firstIndex++;
String result = "";
for (char c : s) {
result+=c;
}
return result;
}
"If the order can be 0-9 first then a-z, then you can use Long.toString(inputNumber, Character.MAX_RADIX), where Character.MAX_RADIX is 36. This gives exactly what you're looking for, except that is uses digits first, then letters."

How to enter characters one by one in to a text field in selenium webdriver?

How to enter characters one by one in to a text field in selenium webdriver? I have used the below code but it's not working
getDriver().findElement(By.id("PhoneNumber")).sendKeys(Keys.chord("9876544322"));
Can anybody suggest how to resolve this?
Here is how I am sending character by character using Selenium Webdriver (in Java). This way in the back-end, I verify at each letter press if the character exists in the input. Normal element.sendKeys() is not working well for me 2 out of 5 times - the last letter is missing, I guess something is buggy with Selenium Webdriver, I don't know. Try the code below, it works 100% of the time for me.
public void TypeInField(String xpath, String value){
String val = value;
WebElement element = driver.findElement(By.xpath(xpath));
element.clear();
for (int i = 0; i < val.length(); i++){
char c = val.charAt(i);
String s = new StringBuilder().append(c).toString();
element.sendKeys(s);
}
}
As you see, I get the value needed to be typed and in the for loop, I take each character, convert it to string and send it to textbox. Also, I have a search for xpath, you can change that to id, or classname, or whatever you want.
If you want to make your sendKeys more human like, I've used something like this:
private static void sendHumanKeys(WebElement element, String text) {
Random r = new Random();
for(int i = 0; i < text.length(); i++) {
try {
Thread.sleep((int)(r.nextGaussian() * 15 + 100));
} catch(InterruptedException e) {}
String s = new StringBuilder().append(text.charAt(i)).toString();
element.sendKeys(s);
}
}
It sends the keys with a 100ms delay, but only an average of 100ms. It creates a normal distribution with average 100ms and std. deviation 15ms.
sendKeys() does enter characters in sequence, but it can at times run quickly enough to be perceived as a copy/paste action. Though, it is in fact intended to simulate a user entering text by typing. Per the sendKeys() JavaDoc:
/** Use this method to simulate typing into an element, which may set its value. */
If you wanted to slow it down, you could make a method that accepts WebElement and String args, convert the String to charsequence[], then use a for loop and enter each index of the array in the .sendKeys() followed by a Thread.sleep(). This seems horribly inefficient, though, as sendKeys() recognizes Strings as charsequence[] (String is a charsequence[] in Java). Adding the Thread.sleep() will only slow your test needlessly.
Honestly, sendKeys() fits your described needs. It's the best way to simulate a user at a keyboard, it just does it really fast.
.chord() will press all keys simultaneously. Not very well suited for field input.
The .sendKeys() method will accept a String. Simply pass your input as such and have a go with it.
driver.findElement(By.id("element")).sendKeys("Field Input Text");
I created a Python function of the Java Selenium code.
Find attached below and tweak based on the elements you want to use:
def TypeInField(xpath, myValue):
val = myValue
elem = driver.find_element_by_xpath(xpath)
c = ""
for i in range(0, len(val)):
c += val[i]
elem.send_keys(c)
time.sleep(3)
elem.send_keys(Keys.ENTER)
I use this function in my test when I want to type a string letter by letter.
public void typeOnLetterByLetter(WebElement webElement, String value, long waitBetweenLetters, ChronoUnit unitTime) {
clear(webElement);
Arrays.asList(value.toCharArray()).forEach(letter -> {
typeOn(webElement, String.valueOf(letter));
pause(waitBetweenLetters, unitTime);
});
}
private void pause(long time, ChronoUnit unitTime) {
try {
Thread.sleep(Duration.of(time, unitTime).toMillis());
} catch (InterruptedException ignore) {
}
}
Here is how I did it in C#
public void TypeInFieldCharByChar(IWebElement element,string text)
{
var strBuilder = new StringBuilder();
for (int i = 0; i < text.Length; i++)
{
Thread.Sleep(100);
strBuilder.Append(text[i]);
element.SendKeys(strBuilder.ToString());
strBuilder.Clear();
}
}

Loops and Strings in Java

It appears to me that there could be a better way to this, maybe using loops, I think.
String hora1 = listaH.get(0);
String hora2 = listaH.get(1);
String hora3 = listaH.get(2);
String hora4 = listaH.get(3);
String hora5 = listaH.get(4);
String hora6 = listaH.get(5);
String hora7 = listaH.get(6);
String hora8 = listaH.get(7);
String hora9 = listaH.get(8);
Is there another way to write this using less words?
Thanks
It depends on what you want to achieve and what you hope to gain from it...but...
Assuming that listaH is java.util.List, you could use
for (String horse : listaH) {
System.out.println(horse);
}
(NB: You can do the same thing with arrays)
Take a look at The for statement and The while and do-while statements for more details
Use arrays for hora variable. It can be like
String[] hora = new String[9];
Now you can use any loops, but for is best for your case.
for(int i = 0; i < 9 ; i++){
hora[i] = listaH.get(i);
}
But why waste resource and complexity on new variables? You can do something like listaH.get(4) wherever you need hora5.
Iterator itr = listaH.iterator();
while(itr.hasNext()) {
String element = (String) itr.next();
System.out.print(element + " ");
}
or
for (int i=0;i<listaH.size();i++) {
String element = (String)listaH.get(i);
System.out.print(element + " ");
}
if you need String array then use this one
String[] array = listaH.toArray(new String[listaH.size()]);
Yes. Instead of separate variables for each element, you should just leave them in the original list and then access them that way. So, for example, when you need the value that you are trying to store in hora9, use listaH.get(8) instead.

Retrieve string from list if we know the index

Is there a way to retrieve a string from an ArrayList if we know the index of the string we want to retrieve? for example in this string:
String text2 = "if(AGE_Y>15){\r\n"
+ "x=PROPERTY_LENGTH;\r\n"
+ "}";
The list this string is added to would look like : if, (, AGE_Y, >, 15, ), {, ...
Now if we loop through the list and add each item to a StringBuilder and define the index for each item:
for (int i = 0; i < list.size(); i++)
{
a=list.get(i);
strBuilder.append(a);
index = strBuilder.length() - a.length();
}
We will know exactly at what index we have the string "PROPERTY_LENGTH" for example.
My question is, how can we retrieve the string "x" from the List? I'm asking because at the line above "x"(or whatever that variable will be named) I want to insert something else in the StringBuilder. Normally we could do something like:
String previous=list.get(indexOfPropertyLength-2);
strBuilder.insert(index-previous.length-1,"string to insert");
Is this at all possible?
Firstly you can rewrite your list iteration to work more efficiently:
for (int i = 0; i < list.size(); i++) {
a=list.get(i);
index = strBuilder.length(); // (you don't have to calculate an index)
strBuilder.append(a);
}
To address the title of your question, if you want to simply find a String in a List given an index, you can of course use myList.get(myIntIndex);. However, it seems that you're asking how to find an index given a String: myList.indexOf(myString) or something of that nature. If you could be a bit clearer, we might be of more help. Are you writing a hybrid assembler or compiler?

Categories

Resources