XCUITest testing a label that includes new lines - xcuitest

I am testing the existence of some screen text on a UILabel. The text I am testing against includes a number of new lines.
If I were to remove the new lines from the string in the codebase the test passes, but with the new lines it doesn't, e.g.
let successMessage = app.staticTexts["Your reward has been saved\nto your account balance.\n\nYou can now view your balance."]
XCTAssertTrue(successMessage.waitForExistence(timeout: 1))
How can I get around this? Putting the expected string into a multiline string didn't work.

I found this too, in the console I could obtain the element with newlines encoded into the subscript as "\n", but when running the test code the same approach did not work.
Subscript Example
app.staticTexts["ABC\nEFG\nXYZ."].firstMatch
Using predicates instead of subscript works for me however:
import XCTest
extension XCUIElementQuery {
func matching(multilineLabel label: String) -> XCUIElementQuery {
matching(NSPredicate(format: "label == %#", label))
}
}
Usage Example:
app.staticTexts.matching(multilineLabel: "ABC\nEFG\nXYZ.").firstMatch

Related

Using regex expression in XCUIElement

In my test app, I don't have a11y identifier set for a button and the a11y label for the button would change based on the response from server. I have created a regex expression for the validation but not sure how to use the regex regression to validate the element, I tried something like below
func testApp() {
let pattern = "User, [A-Za-z0-9]+, will start in [0-9]+ min"
return app.buttons[pattern].firstMatch()
}
The test fails with the above function, failing to identify the element. Does anyone have any thoughts on this?
You should assign an accessibility identifier and then assert the label to test your case properly.
If you still want to use regex, you can use
app.buttons.allElementsBoundByIndex.filter {
$0.label.matches(regex)
}
This line will return an array of XCUIElements.
You will need this String extension
extension String {
func matches(_ regex: String) -> Bool {
return self.range(of: regex, options: .regularExpression, range: nil, locale: nil) != nil
}
}

ObjectMapper explicitly printing carriage returns and escaping quotation marks in my JSON

Been asked to modify a project to save space in our console and log files that has the following Spring application property which I've been asked to leave in place:
spring.jackson.serialization.indent_output=true
Obviously, that's going to pretty print all of our JSON and take up a ton of space in the console and log files.
The application has a groovy class that extends Spring's OncePerRequestFilter class that grabs HTTP requests and responses and sends them through a groovy class that masks sensitive data. This class has a method that takes in a string which is essentially the stringified version of the request's or response's body.
Once the string has been masked, it is run through Jackson's ObjectMapper to basically undo the spring application property to make every print pretty:
Code #1:
return objectMapper.writer().without(SerializationFeature.INDENT_OUTPUT).writeValueAsString(stringToWrite);
Here's some example input that will be going through the code:
{
"person" : {
"personName" : "BAR, FOO",
}
}
... and the result ends up being ...
Result #1:
"{\r\n \"person\" : {\r\n \"personName\" : \"BAR, FOO\",\r\n }\r\n }"
... trying to take the easy way out, I figured applying a simple .replaceAll() on the string would knock out the explicit quote escapes and carriage returns, but I found the carriage returns disappeared and that the quote escapes just come right back...
Code #2:
return objectMapper.writer().without(SerializationFeature.INDENT_OUTPUT).writeValueAsString(stringToWrite.replaceAll("\\\"", '"').replaceAll("\\r", "").replaceAll("\\n", ""));
Result #2:
"{ \"person\" : { \"personName\" : \"BAR, FOO\" } }"
... and finally, if I just use
Code #3
.replaceAll("\\\"", "")
... instead of what I used in Code #2 which was ...
.replaceAll("\\\"", '"')
... then I get ...
Result #3
"{ person : { personName : BAR, FOO } }"
... but what is asked of me is ...
Desired Result:
"{ "person" : { "personName" : "BAR, FOO" } }"
It appears to be a result of something the objectMapper is using, but I'm a little at a loss at this point.
Turns out you can set this, but it isn't obvious.
There is probably a better way, but I found this works:
DefaultPrettyPrinter p = new DefaultPrettyPrinter();
DefaultPrettyPrinter.Indenter i = new DefaultIndenter(" ", "\n");
p.indentArraysWith(i);
p.indentObjectsWith(i);
mapper.setDefaultPrettyPrinter(p);
String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(myObject);
As it turns out, I was complicating this whole situation by thinking it was an issue with objectMapper. Instead, my custom OncePerRequestFilter is logging my custom javax.servlet.http.HttpServletRequestWrapper's requestBody which is a byte[] converted to a String so all I needed was to replace the explicit return carriages and line feeds for UTF-8 encoding:
stringToWrite.replaceAll("\\r", "").replaceAll("\\n", "")

how to get value of an XCUIElement?

How to get the value of textfield in XCODE7 UITesting?
var b = XCUIApplication().childrenMatchingType(.textField).elementBoundByIndex(0).stringValue
Let's suppose you have a Text Field like so:
(I'm hardcoding it's value there ("My Text") for the sake of the example.)
Give it an Accessibility Label. In this case, I'm giving it the "Text Field" label.
Now to access its value, you could do something like:
func testExample() {
let app = XCUIApplication()
let textField = app.textFields["Text Field"]
XCTAssertTrue(textField.value as! String == "My Text")
// So basically "textField.value"
}
On a side note: when in doubt on how to access certain properties of a given XCUIElement, I found that its debugDescription helps a lot.
For example, set a breakpoint after the property declaration, execute the test, wait for the app to stop at the breakpoint, go to lldb console, type po propertyName.debugDescription, and check the output:
I hope that helps.
Lets assume I want to get the string that gets printed for the nav bar of my app
What I did was I created a XCUIElement for my nav bar:
XCUIApplication *app = [[XCUIApplication alloc] init];
XCUIElement *navBarTitle = [app.navigationBars elementBoundByIndex:0];
I then put a breakpoint after the creation of the navBarTitle object and used the debug console to print out the details of the navBarTitle object:
You see in the print out of the debug console that there is a key called identifier.
To extract that string from that object, I created an NSString object using the following method:
NSString *nameofuser = [navBarTitle valueForKey:#"identifier"];
I used the XCUIElement navBarTitle and then used the method valueForKey. valueForKey extracts the string value for the key identifier.
You can read up about this method here:
NSKeyValueCoding
valueForKey is the KEY to unlocking the answer to this question....pun intended :)
app.staticTexts.element(boundBy: 1).label

cordova readAsText returns json string that can't be parsed to JSON object

I read my json file using http and cordova file readAsText functions.
http request returns an object which is ok.
cordova file readAsText function return 'string' which contain extra "r\n\" symbols. This make it impossible to use JSON.parse(evt.target.result)
function readJson(absPath, success, failed){
window.resolveLocalFileSystemURL(absPath, function (entry) {
entry.file(function (file) {
var reader = new FileReader();
reader.onloadend = function (evt) {
success(evt.target.result);
};
reader.readAsText(file);
}, failed);
}, failed);
}
readJson(cordova.file.dataDirectory + 'my.json', function(res){
console.log(JSON.parse(res)); //here I've got an parsing error due to presence of r\n\ symbols
}, failed );
How to read JSON files using cordova?
UPDATE:
funny thing that the following works:
a = '{\r\n"a":"1",\r\n"b":"2"\r\n}';
b = JSON.parse(a);
so the problem not only with \r\n... there is something else that is added by cordova readAsText
UPDATE2
as a workaround I use now var object = eval("(" + res + ")")
Still search for a common way to load json objects...
No one has answered this and I just had to solve it for my project, so I will post my solution.
The readAsText method outputs a string, so you CAN actually run a replace on it, but what you need to do is use a RegExp to find the newline character. here's my solution:
var sanitizerRegex = new RegExp(String.fromCharCode(10), 'g');
var sanitizedData = JSON.parse(result.replace(sanitizerRegex, ''));
I've used the String method fromCharCode to get the specific newline character and the "g" flag to match all instances in the entire string. The problem with your string solution is that you can't do a string replace using the characters for backslash and "n" because the issue is the actual new line character, which is merely represented as "\n".
I do not know the reason JSON.parse can't handle the newline character, or why the file plugin introduces this problem, but this solution seems to work for me.
Also, NEVER use eval like this if you can avoid it, especially on input from a source like a JSON file. Even in a cordova app, using eval is potentially very unsafe.
I found out the solution after debug deeply. readAsText function returned text has one more letter at the first position of text.
Example:
{"name":"John"} => ?{"name":"John"} (?: API didn't return ?, just one string)
I confirmed this with length of result, so we need to use substr(1) before parse JSON.
fileContent = fileContent.substr(1);
var jData = jQuery.parseJSON(fileContent);

f# split html by tags

I would like to parse an HTML document and print each of the paragraphs to a log file as an individual entry. So far I have:
let parseTextFile (path) =
let fileText = File.ReadAllText(path)
fileText.Split('<p>') |> Seq.iter (fun m -> logEmail(m))
But unfortunately for me string.Split does not do what I want here, it seems to exist to split a string by a single character delimiter. How can I split the file up using something more than a single character, it may be nice to have something more than just <p> as well because with just that I will have a </p> at the end of the paragraph. With a regex or some sort of complex matcher I could more specifically pick out everything between <p> tags.
Try using specific libraries for parsing html, for example HtmlAgilityPack.
As wmeyer said, you need to use a different overload of the .Split() method on strings. In fact, the code you posted won't even compile because '<p>' is not a string literal -- you need to use "<p>" instead (single quotes are for character literals).
Here's how to use the correct overload of .Split():
open System.IO
let parseTextFile path =
let fileText = File.ReadAllText path
fileText.Split ([| "<p>"; |], System.StringSplitOptions.RemoveEmptyEntries)
|> Seq.iter logEmail
For a quick test in F# Interactive:
> "First paragraph<p>Second paragraph.<p><p>Third paragraph.<p>"
.Split ([| "<p>"; |], System.StringSplitOptions.RemoveEmptyEntries);;
val it : string [] =
[|"First paragraph"; "Second paragraph."; "Third paragraph."|]
Finally, as #ntr said -- you're much, much better off using a library like the HTML Agility Pack for parsing HTML. Their parsers are very robust and will save you a lot of trouble.