How to get all UI Elements fast - appium

I have an arbitrary app open (can be either android or ios) and I want to get all elements from the UI.
The easy way would be:
appiumWebDriver.findElements(By.xpath("//*")) // this works and returns elements but is slow
However, this is slow and discouraged. Using By.className or other platform dependent strategies would be faster. However, I am not sure how to write a wildcard selector otherwise.
appiumWebDriver.findElements(By.className("*")) // this does not work and returns 0 elements
I would not mind having to differentiate between iOS or Android apps and writing specific code here, but I want a fast and reliable way to get all displayed elements.

The fastest way is the driver.getPageSource() command.
I did some speed tests at my end.
For Android, driver.getPageSource() can be 3x to 6x times faster than using XPath. For the demo app that I used, it took around 0.2 seconds to fetch the page source and around 0.3 to 0.6 seconds to fetch all the elements using XPath.
For iOS, driver.getPageSource() can be 10x to 15x faster than XPath or Predicate Strings. For the demo app that I used, it took around 1-3 seconds to fetch the page source and around 20-30 seconds to fetch the elements using XPath or Predicate String. Predicate String is little bit faster than XPath though.
Two things to note:
Irrespective of what method you use, for iOS, it would give all the elements present on the page. For Android, it would give only those elements that are visible on the screen. If the page is a scrollable page with more elements below the screen, those will not be fetched. You will need to scroll down and then run the command again to fetch these elements.
getPageSource() would give you the XML as a String. You can then use any XML parser to parse it and use it for your purpose.
Hope this helps.
Here's the code that I used to test this for iOS and Android.
public static void main(String[] args) throws Exception {
AppiumDriver driver;
// -> Initialize Android driver
driver = CreateDriverSession.initializeDriver("Android");
driver.findElement(AppiumBy.accessibilityId("Views")).click();
long time1 = System.currentTimeMillis();
driver.getPageSource();
long time2 = System.currentTimeMillis();
System.out.println("GET PAGE SOURCE TIME IN MILLIS" + (time2 - time1));
time1 = System.currentTimeMillis();
driver.findElements(AppiumBy.xpath("//*"));
time2 = System.currentTimeMillis();
System.out.println("XPATH TIME IN MILLIS" + (time2 - time1));
// -> Initialize iOS driver
driver = CreateDriverSession.initializeDriver("iOS");
time1 = System.currentTimeMillis();
String page = driver.getPageSource();
time2 = System.currentTimeMillis();
System.out.println("GET PAGE SOURCE TIME IN MILLIS" + (time2 - time1));
time1 = System.currentTimeMillis();
List<WebElement> elementsUsingXPath = driver.findElements(AppiumBy.xpath("//*"));
time2 = System.currentTimeMillis();
System.out.println("XPATH TIME IN MILLIS" + (time2 - time1));
time1 = System.currentTimeMillis();
List<WebElement> elementsUsingPredicateString = driver.findElements(AppiumBy.iOSNsPredicateString("TRUEPREDICATE"));
time2 = System.currentTimeMillis();
System.out.println("PREDICATE STRING TIME IN MILLIS" + (time2 - time1));
System.out.println("PAGE SOURCE = ");
System.out.println(page);
System.out.println("ELEMENTS USING XPATH = ");
for (WebElement element : elementsUsingXPath) {
System.out.println(element.getText());
}
System.out.println("ELEMENTS USING PREDICATE STRING = ");
for (WebElement element : elementsUsingPredicateString) {
System.out.println(element.getText());
}
}

platform specific code is required. For iOS you can use:
appiumWebdriver.asInstanceOf[IOSDriver].findElements(AppiumBy.iOSNsPredicateString("TRUEPREDICATE"))

Related

casting MapValue to double and multiply to double in dart

Iam new to dart I want a Make table of food program which take an input from user for each food and how much they want but i have an problem with multiply listOfFood[name] with count which both of them double is there any way to solve this ?
double name , count ;
Map<String , dynamic> listOfFood = {
'bacon' :'4.2',
'salad' :'3.5',
'cheaken' :'5.6',
'Goatmeat' :'6.9',
'fish' : '6.5',
};
Map<int , dynamic> food = {
1 : listOfFood ['bacon']
,2 : listOfFood ['salad']
,3 : listOfFood ['cheaken']
,4 : listOfFood ['Goatmeat']
,5 : listOfFood ['fish']
};
stdout.write('please Choose your product \n ${listOfFood}');
name = double.parse(stdin.readLineSync()!);
count = double.parse(stdin.readLineSync()!);
double result = (food[name]) * count;
print(result);
}
You cannot coerce a String that looks like a number to a double in Dart.
Dart has strong typing even when you declare a variable dynamic.
In your case, you're taking the value from listOfFood (not a List, use a better name like priceByFoodName), which is always a String (despite the Map having dynamic values) and multiplying that with a double which can't work.
You should ask yourself why are the prices Strings?
If you want to use them as numbers, just make them numbers.
Also, you don't need dynamic at all. Use types! If you had done that the compiler would've told you right away what the problem was before you even ran the code. That's why types exist.
To solve the problem quickly... this line:
double result = (food[name]) * count;
Should be changed to:
double result = double.parse(food[name]!) * count;
But you should probably change your code quite a bit to handle errors, use appropriate types, stop ignoring nulls etc.

Dart takes too much RAM while generating random numbers and appending them into a file

I am doing a project that involves generating 1 billion random 0/1 numbers and storing them in a file. This is my code:
import 'dart:io';
import 'dart:math';
final int COUNT = 1000000000;
final Random randomizer = new Random();
final File file = new File('./dart/dart.txt');
void main() {
if (file.existsSync()) {
file.deleteSync();
}
file.createSync();
DateTime before = new DateTime.now();
IOSink sink = file.openWrite(mode: FileMode.append);
for (int i = 0; i < COUNT; i++) {
sink.write(nextNumber().toString());
}
sink.close();
DateTime after = new DateTime.now();
print(after.millisecondsSinceEpoch - before.millisecondsSinceEpoch);
}
int nextNumber() {
return randomizer.nextInt(2);
}
It works fine with COUNT=10000, taking a dozen milliseconds to generate 10000 random numbers. However, as I raise COUNT to 1 billion, it took about 25GB of my RAM and ran for more than 40 minutes non-stop. I had to kill the process instead of letting it continue running.
How can I fix this?
P.S. I can't change the programming language since using different programming languages to generate such numbers are one aspect of my project. I also use C++, Java, Javascript, PS, Python, etc. to generate such big data, and none of them met this problem. The final file storing the data is less than 1 GB.

Time to word converter

I was wondering if I could make an app in which time is display in words instead of conventional numbers. Do you think that there is any package for dart to convert time or numbers to words?
For example
1:30 AM will be One : Thirty AM
Thanks for reading this question. Have a wonderful day.
You can use any of available package to convert from number to words example
package: https://pub.dev/packages/number_to_words
import 'package:number_to_words/number_to_words.dart';
main(){
String input = '1:30 AM';
var s1 = input.split(':');
var s2 = s1[1].split(' ');
String hour = s1[0];
String minute = s2[0];
String hourWord = NumberToWord().convert('en-in',int.parse(hour));
String minuteWord = NumberToWord().convert('en-in',int.parse(minute));
print('$hourWord:$minuteWork ${s2[1]}');
}

How to filter tweets via the since/until parameters in Twitter4J queries?

I m listening twitter streams by coordinate using twitter4j without any input keyword, as i know twitter api just gives tweets of last 7 days. My code was working funny and i had an network problem and i could not get streams of 2 days ago. i need to get tweets of 22.04.2018 and 23.04.2018. I need to use until parameter to get these tweets but i could not see any sample of using until parameter without any input query. The following is my code statement when i listen for streams in given coordinates, how can i add until parameter to get past 2 days streams.
double latitude1 = 36.000000;
double longitude1 = 26.000000;
double latitude2 = 42.000000;
double longitude2 = 45.000000;
double[][] latlong = {{longitude1, latitude1}, {longitude2, latitude2}};
twitterStream.addListener(listener);
FilterQuery fq = new FilterQuery();
fq.locations(latlong);
twitterStream.filter(fq);
So the question is how can i use FilterQuery with until,
i tried something like as follows ;
MyConnectionBuilder myConnection = new MyConnectionBuilder();
Twitter twitter = new
TwitterFactory(myConnection.configuration.build()).getInstance();
double latitude1 = 42.000000;
double longitude1 = 45.000000;
Query query = new Query();
GeoLocation obj = new GeoLocation(latitude1, longitude1);
query.setGeoCode(obj, 2000, Unit.valueOf("km"));
query.setSince("20180421");
query.setUntil("20180422");
query.setLang("tr");
QueryResult result = twitter.search(query);
for (Status status : result.getTweets()) {
System.out.println("#" + status.getUser().getScreenName() + ":" + status.getText());
}
but nothing comes from result query, what can be wrong with these code statements ?
The official JavaDoc of Twitter4J's Query class tells us for setSince:
If specified, returns tweets with since the given date.
Date should be formatted as YYYY-MM-DD
So have to set a date in the String format YYYY-MM-DD, e.g., "2018-04-21". Note well, that dashes are used here which you did not provide in your original code snippet.
Analogously, you have to apply this date pattern style to setUntil:
If specified, returns tweets with generated before the given date.
Date should be formatted as YYYY-MM-DD
Hope this helps.

How to fix C-style for statement has been removed in Swift 3?

I am converting old Swift 2 code to Swift 3 and I am facing difficulty in converting following for loop
for (var nSize = merkleTree.count; nSize > 1; nSize = (nSize + 1) / 2)
{
//...
}
There are many similar question on SO but I didn't find any solution applicable to my problem Or I didn't understand.
I thought that below code will work but it is giving error.
for var nSize in merkleTree.count.stride(to:1, by:(nSize+1)/2)
Use of unresolved identifier 'nSize'
I don't think this can be written using for anymore, but you can use while loop to get the job done:
var nSize = merkleTree.count
while nSize > 1 {
// loop body
nSize = (nSize + 1) / 2
}
I would expect stride not to work in this case, because as your error states, you cannot use nSize as the stride parameter - nSize is iterating variable that gets declared based on the range, so you need the range to exist. At least that's my interpretation of the error (I know that theoretically you can generate range based on the previously generated item, but obviously stride does not work that way).
I believe you can find a way to generate a proper array of values using reduce (because I was able to, see below, maybe you can make it simpler), or by implementing your own stride that would accept a closure instead of a step (which would allow you to compute next item based on previous one), but both approaches are more complicated and obscure than using the simple while loop, so I personally prefer the while loop.
My not so nice reduce implementation (in result it uses an array and not a range, since by looking at NSRange I don't think you can create a range that does not step by 1):
let merkleTree = [1,2,3,4,5,6,7,8,9]
let numberOfDivisions = Int(log2(Double(merkleTree.count))) + 1
let startValue = merkleTree.count
let nSizes = (0..<numberOfDivisions).reduce([startValue]) { (result, next) -> [Int] in
var newResult = result
newResult.append((result.last! + 1) / 2)
return newResult
}
print(nSizes)
// and now you can for-in it:
for nSize in nSizes {
// ...
}

Resources