For example, I'm testing a search page, which will show the result numbers in .text > span:nth-child(1).
However, if not any result, it will only show text="nothing" or .text > span:nth-child(1) is not exist.
So how can I wait for both conditions?
You need to use a comma-separated list of CSS selectors wich will match all elements that can be selected by one of the selectors in that list:
// ↓ comma
await page.locator('.text > span:nth-child(1), span:has-text("nothing")').innerText();
It will wait for either .text > span:nth-child(1) or span:has-text("nothing").
You can wait for both elements, using a try-catch, and setting a boolean variable, depending on which element is being found.
In Java you could use something like
Boolean nothingFound, resultsFound = false;
try {
page.waitForSelector("text=\"nothing\"");
nothingFound = true;
} catch (Exception e) {}
try {
page.waitForSelector(".text > span:nth-child(1)");
resultsFound = true;
} catch (Exception e) {}
I'm not a Javascript expert, but I think something like this should work:
let nothingFound, resultsFound = false;
try {
await page.waitForSelector('text="nothing"');
nothingFound = true;
}
catch (e) {}
try {
await page.waitForSelector('.text > span:nth-child(1)');
resultsFound = true;
}
catch (e) {}
Related
I'm trying to transform a Stream of a list of one type into a Stream of a list of another type, and having an issue with this.
I have this list of Habits that I'm streaming from Firebase, and I want to accept that stream in a function, and return a new stream that is a list of ViewModels of another type from it. But my function is returning a stream of the wrong type.
Here is my code:
Stream<List<HabitCompletionViewModel>> _getTodaysHabits(
Stream<List<Habit>> habitsStream) {
var result = habitsStream.map((habitsList) {
habitsList.map(
(habit) async {
await _getHabitCompletionsCurrent(habit);
HabitCompletion completion = habit.completions!.firstWhere(
(completion) => completion.date
.dayEqualityCheck(DateTime.now().startOfDate()));
return HabitCompletionViewModel(completion: completion, habit: habit);
},
).toList();
});
return result;
}
I am getting a compile error because the result variable is showing as type Stream<Null> when I hover over it, where I would expect it to be Stream<List<HabitCompletionViewModel>>. Any idea what I'm doing wrong?
Your outer .map call does not have a return statement which is why you are getting a Stream<Null>.
So add a return statement like so:
Stream<List<HabitCompletionViewModel>> _getTodaysHabits(
Stream<List<Habit>> habitsStream) {
var result = habitsStream.map((habitsList) {
// added return statement here
return habitsList.map(
(habit) async {
await _getHabitCompletionsCurrent(habit);
HabitCompletion completion = habit.completions!.firstWhere(
(completion) =>
completion.date.dayEqualityCheck(DateTime.now().startOfDate()));
return HabitCompletionViewModel(completion: completion, habit: habit);
},
).toList();
});
return result;
}
However the above code still has an error because it is now returning a Stream<List<Future<HabitCompletionViewModel>>> instead of the desired Stream<List<HabitCompletionViewModel>>. To solve this you can use .asyncMap instead of .map.
Stream<List<HabitCompletionViewModel>> _getTodaysHabits(
Stream<List<Habit>> habitsStream) {
var result = habitsStream.asyncMap((habitsList) {
return Stream.fromIterable(habitsList).asyncMap(
(habit) async {
await _getHabitCompletionsCurrent(habit);
HabitCompletion completion = habit.completions!.firstWhere(
(completion) =>
completion.date.dayEqualityCheck(DateTime.now().startOfDate()));
return HabitCompletionViewModel(completion: completion, habit: habit);
},
).toList();
});
return result;
}
I do not know why catch statement does not catch thrown error when I debug the app.
This is the main function:
void main() async {
final initialState = await persistor.load();
bool logged = false;
if (initialState.isLoggedIn) {
logged = await initialState.silentlyLogin(); // <---- FUNCTION THAT THROWS ERROR
}
if (!logged) {
initialState.logout();
}
}
This is the silentlyLogin function of my State class:
Future<bool> silentlyLogin() async {
try {
await globals.googleSignIn.signInSilently();
return true;
} catch (e) {
return false;
}
}
In debug the googleSignIn.signInSilently function thrown an error, in this part of code:
#override
dynamic decodeEnvelope(ByteData envelope) {
// First byte is zero in success case, and non-zero otherwise.
if (envelope.lengthInBytes == 0)
throw const FormatException('Expected envelope, got nothing');
final ReadBuffer buffer = ReadBuffer(envelope);
if (buffer.getUint8() == 0)
return messageCodec.readValue(buffer);
final dynamic errorCode = messageCodec.readValue(buffer);
final dynamic errorMessage = messageCodec.readValue(buffer);
final dynamic errorDetails = messageCodec.readValue(buffer);
if (errorCode is String && (errorMessage == null || errorMessage is String) && !buffer.hasRemaining)
throw PlatformException(code: errorCode, message: errorMessage, details: errorDetails); // <------ HERE IS THE ERROR
else
throw const FormatException('Invalid envelope');
}
In the debug mode, android studio blocks the app in the throw PlatformException line, but my catch statement is never catched, so my function always returns true.
While my catch statement is never catched.
The exception is probably thrown in native code and not passed to Dart at all. Dart can't catch Java or ObjectivC/Swift exceptions. The plugin would need to catch it in Java, send a message to Dart and in Dart an artificial exception would need to be thrown.
See also
https://github.com/flutter/flutter/issues/17677
https://github.com/flutter/flutter/issues/19748
https://github.com/flutter/flutter/issues/28430
Based on the SharedPreferences class, I try to retrieve a preference value like so:
String loadIPAddress() {
SharedPreferences.getInstance().then((SharedPreferences prefs) {
try {
var loadedValue = prefs.getString('serverIPAddress');
print('loadIPAddress <= ' + loadedValue);
return loadedValue; // [1]
} catch (e) {
print('loadIPAddress <= NOPE');
return '---'; [2]
}
});
}
Unfortunately, this doesn't return a value each time.
Q: Does the 1 and [2] return statements return the value of loadIPAddress()?
No, as you've guessed, those returns return from the then callback.
To return from loadIPAddress, refactor it like this:
Future<String> loadIPAddress() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
try {
var loadedValue = prefs.getString('serverIPAddress');
print('loadIPAddress <= ' + loadedValue);
return loadedValue;
} catch (e) {
print('loadIPAddress <= NOPE');
return '---';
}
}
Note that having made loadIPAddress async, it now returns a Future, so you should call it like:
String ip = await loadIPAddress();
// or
loadIPAddress().then((String ip) {
// do something with ip - probably setState
});
I got Page source using
String pageSource = driver.getPageSource();
Now i need to save this xml file to local in cache. So i need to get element attributes like x and y attribute value rather than every time get using element.getAttribute("x");. But I am not able to parse pageSource xml file to some special character. I cannot remove this character because at if i need element value/text it shows different text if i will remove special character. Appium is use same way to do this.
I was also facing same issue and i got resolution using below code which i have written and it works fine
public static void removeEscapeCharacter(File xmlFile) {
String pattern = "(\\\"([^=])*\\\")";
String contentBuilder = null;
try {
contentBuilder = Files.toString(xmlFile, Charsets.UTF_8);
} catch (IOException e1) {
e1.printStackTrace();
}
if (contentBuilder == null)
return;
Pattern pattern2 = Pattern.compile(pattern);
Matcher matcher = pattern2.matcher(contentBuilder);
StrBuilder sb = new StrBuilder(contentBuilder);
while (matcher.find()) {
String str = matcher.group(1).substring(1, matcher.group(1).length() - 1);
try {
sb = sb.replaceFirst(StrMatcher.stringMatcher(str),
StringEscapeUtils.escapeXml(str));
} catch (Exception e) {
e.printStackTrace();
}
}
try {
Writer output = null;
output = new BufferedWriter(new FileWriter(xmlFile, false));
output.write(sb.toString());
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if you will get that kind of problem then catch it with remove special character and parse again.
try {
doc = db.parse(fileContent);
} catch (Exception e) {
removeEscapeCharacter(file);
doc = db.parse(file);
}
It might works for you.
I can able to do same using SAXParser and add handler to do for this.
Refer SAX Parser
After many attempts to get the content of the response in HttpRequest, I failed completely to know or understand why I can't have what I want, and I must mention that I can log and manipulate the response only inside an onReadyStateChange (onLoad and onLoadEnd are giving me the same results!), but I really want that value outside the callback.
Here is the part of code that I'm stuck with
Map responsData;
req=new HttpRequest()
..open(method,url)
..send(infojson);
req.onReadyStateChange.listen((ProgressEvent e){
if (req.readyState == HttpRequest.DONE ){
if(req.status == 200){
responsData = {'data': req.responseText};
print("data receaved: ${ req.responseText}");
//will log {"data":mydata}
}
if(req.status == 0){
responsData = {'data':'No server'};
print(responsData );
//will log {"data":No server}
}
}
});
//anything here to get responsData won't work
You have to assign an onLoad callback before you call send.
I'm not sure what you mean with only inside an onReadyStateChange.
Maybe you want to assign the responseText to a variable outside the the callback.
Create a method:
Future<String> send(String method, String url, String infojson) {
var completer = new Completer<String>();
// var result;
req=new HttpRequest()
..open(method,url)
..onLoad.listen((event) {
//print('Request complete ${event.target.reponseText}'))
// result = event.target.responseText;
completer.complete(event.target.responseText);
})
..send(infojson);
return completer.future;
}
and call this method like
var result;
send(method, url).then(
(e) {
// result = e;
print('Request complete ${e}'));
});