I have an variable that's of type .Image and class XCUIElement. Something like this:
var image = app.descendantsMatchingType(.Image).elementAtIndex(0)
Is there a way I can extract the actual image so I can compare it to another image?
I've tried caling the value method, but it returns a string. Casting it to a UIImage always fails.
I have had a conversation about this with the Apple Developer Tools evangelist recently. There is currently no way of accessing the actual image from an image view, button, etc. Similarly, there is no way to access other properties of views that might be of interest, like "isHidden" or "attributedText", etc. I was told that the engineers on the UI Testing team are interested in the use cases that people are wanting access to these properties for, so it would be very helpful -- both for them and for the other people who want this feature -- if you would file a bug report / feature request asking for it at https://bugreport.apple.com
As a tip regarding the "value" property on an XCUIElement, at least for now this appears to map to the "accessibilityValue" property of whatever view the XCUIElement is referencing. So if you set that accessibilityValue of a view you are interested in to contain some information you are interested in verifying, then this can possibly help in testing. Two things to be aware of though:
1) Even though the "value" property of an XCUIElement is of type "id", the type of the accessibilityValue property is "NSString". I don't know what would happen if you try to force some non-string value (like an image) into accessibilityValue and then try to retrieve it from the "value" property of XCUIElement, but I suspect it wouldn't work well. Partially because:
2) The accessibilityValue property of a view is actually used by Apple's VoiceOver feature for the vision impaired. When the value is set, it will be read out loud when the user taps on that element (which is why it's supposed to be a string).
I also covered the issue with not being able to access properties of view via XCUIElement in more detail here: http://www.danielhall.io/exploring-the-new-ui-testing-features-of-xcode-7
I know it may be not exactly what you're looking for, but I managed to write a test that checks if the visual representation of a UIImage on the screen has changed.
I'm using a screenshot() method of XCUIElement which returns an instance of XCUIScreenshot:
let myImage = XCUIApplication().images["myAccessibilityIdentifier"]
let screenshotBefore = myImage.screenshot()
//...
//do some actions that change the image being displayed
//...
let screenshotAfter = myImage.screenshot()
//Validating that the image changed as intended
XCTAssertNotEqual(screenshotBefore.pngRepresentation, screenshotAfter.pngRepresentation)
The screenshots will be the size of the image as rendered on the screen which may be different to the original image of course.
It's important to compare the PNG representations using the pngRepresentation property, but not the XCUIScreenshot objects because the two objects will always be different internally.
This technique can't test that the image displayed on the screen is exactly what is needed but at least can detect changes in the image.
Related
I'm trying to implement a very simple test, but I'm stuck with XCUIElementQuery.matching(identifier:) method. I think querying UIImageViews is not working as expected, but not sure why.
I have these two settings set for views:
Label:
UIImageView:
And I have this test:
func testRecording() {
XCTAssertEqual(app.staticTexts.matching(identifier: "label").count, 2)
XCTAssertEqual(app.images.matching(identifier: "accept").count, 1)
}
where I'm querying for these two views (Label with id label and UIImageView with id accept). From here: app is simply defined as class property like this: let app = XCUIApplication().
So, this is what screen looks like:
This means that this test should pass since there are two staticTexts with id label and one image with id accept on screen. staticTexts are being successfully queried, but it is failing when matching(identifier:) tries to query image with id accept:
I tried to query with many things, like:
app.images.
app.tables.
app.tables.images.
app.tables.cells.images.
app.buttons. (also marked as Button in Traits part of the Accessibility settings)
app.staticTexts. (also marked as Static Text in Traits part of the Accessibility settings)
but no luck... Is there anything that I'm doing wrong here, or missing something?
OK, since I'm in the QA team, I haven't looked much to the dev code. But when I did, I found the answer.
UIImageView that is used for displaying check mark is never being used from .xib file, but instead is always allocated a new instance like this:
indicatorView = UIImageView(image: #imageLiteral(resourceName: "arrow_marked_ok_small"))
meaning that new accessibility options are set with initializer call, so that old UIImageView instance that has accessibility options from .xib file I posted in first two images is being overridden.
How can you programmatically get the background color of a WKInterfaceGroup object?
You can set the value using
- (void)setBackgroundColor:(nullable UIColor *)color;
but can you also get it?
You can set values on an interface object, but you can't get the current values of its attributes.
This answer can be found in the WKInterfaceObject superclass documentation:
Communication between an interface object in your extension and the corresponding interface element in your Watch app is one way. You can set the values of an interface object, but you cannot get the current values. If you want to know the current value of an attribute, you must save the value yourself.
Here is an example showing how to maintain your own value for the group color:
#IBOutlet weak var myGroup: WKInterfaceGroup!
var myGroupBackgroundColor: UIColor?
// Set the group's color, also save the color for future reference
myGroupBackgroundColor = UIColor.blue()
myGroup.setBackgroundColor(myGroupBackgroundColor)
You can then get the group's current color using your myGroupBackgroundColor property.
It doesn't look like there is a way to GET the background Color.
I've never used any WatchKit stuff before, but after looking at the WKInterfaceGroup class and reading up on it a little bit, here is my understanding of how WatchKit appears to work.
It looks like Apple wants communication (at least in the scope of the files I've looked at) to be one way. That is, they don't want you to retrieve things like that from the watch to the app level. They want you to tell the watch what to do, and the watch only receives these things (like color, inset, corner radius, etc.)
This is the conclusion I have come to (not sure if I'm right). Now, if you NEED to get the background color from the WKInterfaceGroup, then you can keep trying to find a solution. I do not know if it is possible. What I would recommend is trying to find an alternate solution to needing this property from WKInterfaceGroup.
The question is actually really simple:
Is there a way to assert the displayed value from a specific label (e.g. UILabel) when using an accessibility label on this object?
As far as I see it, all the assertions (e.g. XCTAssertEquals) made in the examples, be it from a WWDC Talk or Blogposts, are only checking if an element exists for a query like XCTAssertEquals(app.staticTexts["myValue"].exists, true) or if the number of cells in a table is correct XCTAssertEquals(app.tables.cells.count, 5). So, when avoiding accessibility labels it's possible to check if an object has a certain value displayed, but not which object / element.
And when using accessibility labels, it robs me of the opportunity to query against the displayed values, because app.staticTexts["myValue"] will now fail to deliver a result but app.staticTexts["myAccessibilityLabel"] will hit.
Assuming I want to test my "Add new Cell to Table" functionality, I can test that there is really a new cell added to the list, but I have no idea if the new cell is added at the top or the bottom of the list or somewhere in between.
For me, an easy way to check if a specific element has a certain value should be a no-brainer when it comes to UI Testing.
It is possible that due to the missing documentation I might overlook the obvious. If so, just tell me.
Be sure to set the .accessibilityValue property of the UILabel whenever you set its .text value. Then in UITest, you can test the accessibility value like this:
let labelElement = app.staticTexts["myLabel"]
...
XCTAssertEqual(labelElement.value as! String, "the expected text")
I think you are asking a few different things, so I will try to answer each question individually.
Is there a way to assert the displayed value from a specific label (e.g. UILabel) when using an accessibility label on this object?
In short, no. UI Testing works by hooking into accessibility APIs, so you are limited to querying for objects based on that. You can, however, check the -value property of certain elements, such as controls. This is used to test if a switch is on or off. Note that these boil to down using accessibility APIs as well, just a different method (-accessibilityValue over -accessibilityIdentifier and -accessibilityLabel).
...but I have no idea if the new cell is added at the top or the bottom of the list or somewhere in between.
To interrogate an XCUIElement for its frame you can use the new XCUIElementAttributes protocol which exposes -frame. For example:
let app = XCUIApplication()
app.launch()
app.buttons["Add New Cell to Table"].tap()
let lastCell = app.cells["Last Cell"]
let newCell = app.cells["New Cell"]
XCTAssert(newCell.exists)
XCTAssert(newCell.frame.minY > lastCell.frame.maxY)
For me, an easy way to check if a specific element has a certain value should be a no-brainer when it comes to UI Testing.
If you think of everything in terms of accessibility this becomes a non-issue. UI Testing can only interact with your elements via accessibility APIs, so you must implement them. You also get the added benefit of making your app more accessible to user's with those settings enabled.
Try setting both the -accessibilityLabel or -accessibilityIdentifier for the cell to the displayed text. UI Testing can be finicky as to which one it uses.
It is possible that due to the missing documentation I might overlook the obvious. If so, just tell me.
XCTest and UI Testing don't have any official documentation. So I've gone and extracted my own from the header files exposed in the framework. Note than even though they were pulled from source, they are unofficial.
XCTest / UI Testing Documentation
What works for me is to set the accessibility identifier of the UILabel to let's say MyLabel.
func myLabelText() -> String {
let myLabelUIElement: XCUIElement = self.application.staticTexts["MyLabel"]
return myLabelUIElement.label
}
Tested with Xcode 8 and iOS 10
From the apple forums it looks like it is possible to get the value of the label:
The only way I've found is to not set an Accessibility Label, but use identifier instead. Then XCUIElement.label will change to match the current text of the label.
However there is a gotcha: if you have previously set Accessibility Label in XC, and remove it, an entry setting the label to "" remains in the storyboard. In this case, not only will calling .label will return "", but you won't be able to query for the label by it's text!
The only thing you can do is delete and re-add the label, or manually edit the xml.
lastobelus - https://forums.developer.apple.com/thread/10428
I have a custom UITextInput-based text editor. It works very well, except for multi-stage input via marked text.
My marked region renders correctly, and marked text is inserted, but the candidate list above the keyboard is blank.
For example, here is the Japanese (Kana) keyboard showing suggestions on a standard UITextView:
And here is my custom editor displaying the same marked text:
I have spent several days debugging this issue and have found that the cause is private class UIKeyboardImpl returning NO for the method delegateSupportsCorrectionUI
If I override this method in a category on UIKeyboardImpl and return YES instead, then multistage input suggestions correctly display for my text editor. However this does not address the underlying cause of the problem (and it's not usable).
I have also looked very closely at Apple's SimpleTextInput sample code. This implements a basic Core Text editor. SimpleTextInput correctly displays multistage input suggestions, however I cannot seem to find a single difference in its implementation of UITextInput that causes it to work and mine to break.
(In fact, I am unable to "break" the SimpleTextInput sample's ability to display multi-stage input. Which leads me to think that my focus on the UITextInput implementation is the wrong track. And it is something else altogether.)
Okay, this is a bit embarrassing. I just now noticed:
#property(nonatomic, readonly) UIView *textInputView
Discussion
The view that both draws the text and provides a coordinate system for
all geometric values in this protocol. (This is typically an instance
of the UITextInput-adopting class.) If this property is unimplemented,
the first view in the responder chain is selected.
In the documentation.
I had stupidly #synthesize'd this property and forgot about it, meaning my UITextInput implementation was returning a nil textInputView. Simply leaving it unimplemented chooses the first view from the responder chain as described, which provides the text system with the necessary coordinate system to handle auto-correction and multistage input suggestions.
This was after three days of debugging. Now I feel stupid.
In Xcode 5, we can now hover over a UIImageView variable to get a Quick Look of the image. If I want to see the image of an image view whose variable I don't have direct access to, but I have the hex address, is there a way to show it? i.e., If I know there's a UIImageView at 0x12193fb0, doing po 0x12193fb0 in the debugger will print out the info about the object. Is there a similar way to Quick Look by address? Using the Variables View next to the debugger isn't an option, since I'm breaking the program manually, so it's not in the context of the object that owns the image view.
You can add a watch for your variable (Debug area, variables view). You just have to cast it if you only have the pointer address. Eg.
(NSString*)0x1234567
The watch can be added from the context menu.
Quick update:
Just to clarify myself, you can get to the following just by knowing the address. In this instance I just typed (BNMap*)0xb4b5dd0 as the expression.