Testing if an element is visible with Xcode 7 UITest - xcode

I want to verify if an element is visible or not depending on its .hidden property but I don't find a valid way to do that using the new Xcode 7 UI test stuff.
I've tried with myelement.exists and myelement.hittable but they doesn't seems to work as I expected. I suppose they work on conjunction with the hidden property. An hidden element shouldn't exists and is not hittable... but this is not the current behaviour (I can understand the exists behaviour... but a hidden element should be not hittable IMO).
Is there another way to verify the "hidden" property value?

As of Xcode 7.1 Beta 3, UI Testing does not currently support validating the visibility of an element. I suggest filing a radar to bring the necessary attention to Apple.
Xcode 7.1 has fixed this issue. hittable now checks to see if the element is correct.

1) I am testing the UI with swift in Xcode 7.3 and I using both .hittable and .exists for testing whether a label is hidden or not and they both work. I test for 'true' and 'false' to make sure either way agree with the result.
I have a label whose static text is "Track Info" and set to be hidden when app is first loaded, then later on I press a button to show the label, and here is the result after the label is shown.
// test fails
let trackInfoLabel = app.staticTexts["Track info"]
XCTAssertEqual(trackInfoLabel.exists, true)
XCTAssertEqual(trackInfoLabel.hittable, true)
// test passes
XCTAssertEqual(trackInfoLabel.exists, false)
XCTAssertEqual(trackInfoLabel.hittable, false)
// test passes
let trackInfoLabel = app.staticTexts["Track Info"]
XCTAssertEqual(trackInfoLabel.exists, true)
XCTAssertEqual(trackInfoLabel.hittable, true)
// test fails
XCTAssertEqual(trackInfoLabel.exists, false)
XCTAssertEqual(trackInfoLabel.hittable, false)
Leter on when I press the button to hide the label, all the results turned opposite. This confirms that both properties (hittable and exists) works for label.hidden setting.
2) Another way to find out if an element is hidden, you can do is element.frame.size.width == 0 || element.frame.size.height == 0

XCUIElement.hittable works for me (in my particular test case where I am checking several UIButton elements for visibility) - quite sure it is not a right way to do it though

Next code worked for me.
At the end of the test you can past code as follow:
while ([app.staticTexts matchingIdentifier:#"accesibilityId"].count > 0) {
sleep(1);
}

I agree hittable doesn't always work for buttons (Swift 2.0, XCode 7.2)
I just discovered that if button is visible, you can find it ONLY among buttons, while if button is hidden, you can find it's tag in staticTexts as well!
XCTAssertFalse(app.buttons["Log out"].exists && app.staticTexts["Log out"].exists) // This button is visible (hidden = false)
XCTAssert(app.buttons["Log in"].exists && app.staticTexts["Log in"].exists) // This one is hidden
Hacky, but works for buttons. Apple should just introduce .hidden or .visible along .hittable and .exists

The syntax is now .isHittable:
isHittable only returns true if the element is already visible and
hittable onscreen. It returns false for an offscreen element in a
scrollable view, even if the element would be scrolled into a hittable
position by calling click(), tap(), or another hit-point-related
interaction method.
Using the .isHittable property will work because hidden elements are not visible or hittable on screen.

My solution is to add accessibilityIdentifier dynamicly
func someMethod() {
label.isHidden = true
label. accessibilityIdentifier = "isHidden"
}
func someOtherMethod {
label.isHidden = false
label. accessibilityIdentifier = "isVisible"
}
and the in UITest you can use it as
if app.staticTexts["MyLabel"].identifier == "isHidden" {
dosomething()
}

Related

NSOutlineView reloadItem/reloadData not working when replacing an item

I have a view-based NSOutlineView with a dataSource/delegate model instead of binding to a tree controller (I want control over the insert/update animations).
I'm replacing an item in my model and would like to update that specific row in the outline view without having to call reloadData().
I cannot get this to work. Either the item does not update at all or the item's expanded state doesn't update. There seems to be some caching being done inside of NSOutlineView according to this, but even with these suggestions, I could not get it to work.
What I have:
(1) The outline view represents a folder structure
(2) At first, there is a singe file:
(3) The file is then replaced with a folder item:
// Model update
let oldFileItem = rootItem.children.first!
rootItem.children.remove(at: 0)
rootItem.children.append(Item(title: "Folder", children:[], isExpandable:true))
Expected result:
Actual result (reloadItem):
outlineView.reloadItem(oldFileItem) // I kept a reference
Icon and title reloaded, but note that the expansion triangle is missing.
I can somewhat understand that reloadItem() might not work in this case, because the old item is not part of the data model anymore. Strangely enough, the item's title and icon update, but not the expansion state.
Actual result (reloadData(forRowIndexes:columnIndexes:):
outlineView.reloadData(forRowIndexes: IndexSet(integer:0), columnIndexes: IndexSet(integer:0))
No effect whatsoever. This is the one that I would have expected to work.
Actual result (remove/insert):
outlineView.removeItems(at: IndexSet(integer:0), inParent: rootItem, withAnimation: [])
outlineView.insertItems(at: IndexSet(integer:0), inParent: rootItem, withAnimation: [])
No effect whatsoever.
The docs say about removeItems(): "The method does nothing if parent is not expanded." and isExpanded does indeed return false for the root node, although its children are visible. Is this special behavior for items that are direct children of the root node? What am I missing here?
For reference, my data model:
class Item:NSObject {
var title:String
var children:[Item]
var isExpandable:Bool
init(title:String, children:[Item], isExpandable:Bool) {
self.title = title
self.children = children
self.isExpandable = isExpandable
}
}
For reference:
It turned out to be an issue with how I used the API. NSOutlineView.removeItems/insertItems expect nil for the inParent parameter for the root item. I was handing in the actual root item. Using nil instead of the root item solved the problem.

Xcode UITest scrolling to the bottom of an UITableView

I am writing an UI test case, in which I need to perform an action, and then on the current page, scroll the only UITableView to the bottom to check if specific text shows up inside the last cell in the UITableView.
Right now the only way I can think of is to scroll it using app.tables.cells.element(boundBy: 0).swipeUp(), but if there are too many cells, it doesn't scroll all the way to the bottom. And the number of cells in the UITableView is not always the same, I cannot swipe up more than once because there might be only one cell in the table.
One way you could go about this is by getting the last cell from the tableView. Then, run a while loop that scrolls and checks to see if the cell isHittable between each scroll. Once it's determined that isHittable == true, the element can then be asserted against.
https://developer.apple.com/documentation/xctest/xcuielement/1500561-ishittable
It would look something like this (Swift answer):
In your XCTestCase file, write a query to identify the table. Then, a subsequent query to identify the last cell.
let tableView = app.descendants(matching: .table).firstMatch
guard let lastCell = tableView.cells.allElementsBoundByIndex.last else { return }
Use a while loop to determine whether or not the cell isHittable/is on screen. Note: isHittable relies on the cell's userInteractionEnabled property being set to true
//Add in a count, so that the loop can escape if it's scrolled too many times
let MAX_SCROLLS = 10
var count = 0
while lastCell.isHittable == false && count < MAX_SCROLLS {
apps.swipeUp()
count += 1
}
Check the cell's text using the label property, and compare it against the expected text.
//If there is only one label within the cell
let textInLastCell = lastCell.descendants(matching: .staticText).firstMatch
XCTAssertTrue(textInLastCell.label == "Expected Text" && textInLastCell.isHittable)
Blaines answer lead me to dig a little bit more into this topic and I found a different solution that worked for me:
func testTheTest() {
let app = XCUIApplication()
app.launch()
// Opens a menu in my app which contains the table view
app.buttons["openMenu"].tap()
// Get a handle for the tableView
let listpagetableviewTable = app.tables["myTableView"]
// Get a handle for the not yet existing cell by its content text
let cell = listpagetableviewTable.staticTexts["This text is from the cell"]
// Swipe down until it is visible
while !cell.exists {
app.swipeUp()
}
// Interact with it when visible
cell.tap()
}
One thing I had to do for this in order to work is set isAccessibilityElement to true and also assign accessibilityLabel as a String to the table view so it can be queried by it within the test code.
This might not be best practice but for what I could see in my test it works very well. I don't know how it would work when the cell has no text, one might be able to reference the cell(which is not really directly referenced here) by an image view or something else. It's obviously missing the counter from Blaines answer but I left it out for simplicity reasons.

XCUIElement exists, but is not hittable

In my UI tests, I create a UIView programmatically using (shortened)
let topMarker = UIView.init(frame: CGRect.init())
…
topMarker.accessibilityIdentifier = kTopMarker
The topMarker view is in a custom table view cell.
In my UI tests, I use
let new1stCell = app.cells.element(boundBy: 0)
let topMarker = new1stCell.otherElements[kTopMarker]
let topMarkerExists = topMarker.waitForExistence(timeout: 15)
XCTAssertTrue(topMarkerExists, "Top marker does not exist")
XCTAssertTrue(topMarker.isHittable, "Top marker is not hittable")
When I set a Test Failure breakpoint, the test stops at the last line, i.e. topMarker exists, but is not hittable.
On the other hand, I can see the view in the snapshot, i.e. it exists and is visible.
This is stange, because the docs say:
isHittable returns true if the element exists and can be clicked, tapped, or pressed at its current location. It returns false if the element does not exist, is offscreen, or is covered by another element.
I thought, maybe it is visible, but cannot be clicked, tapped, or pressed, because userInteractionEnable is not true, but even if I set this property to true, the view does not become hittable.
What am I missing?
Problem solved:
An XCUIElement is only hittable, if its isAccessibilityElement property is set to true.
The docs to property isAccessibilityElement say
The default value for this property is false unless the receiver is a standard UIKit control, in which case the value is true.
Assistive applications can get information only about objects that are represented by accessibility elements. Therefore, if you implement a custom control or view that should be accessible to users with disabilities, set this property to true.
My UIView that I instantiated programmatically is not a standard UIKit control. As soon as I added
topMarker.isAccessibilityElement = true
the test
XCTAssertTrue(topMarker.isHittable, "Top marker is not hittable")
succeeded.

Why UIBarbuttonItem is not working in Swift while code is written well?

I have written the same behaviour in two ways. The first one, doesn't work:
var barbutton = (left) ? navigationItem.leftBarButtonItem : navigationItem.rightBarButtonItem
barbutton = UIBarButtonItem(customView: button)
opposite to:
if (left) {
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: button)
} else {
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button)
}
What is the difference?
As #Martin points out, the two code sequences are very different in function.
The first sets barbutton to the contents of either leftBarButtonItem or rightBarButtonItem and then discards that value and sets (the temporary variable) to a newly created button.
The second sets either leftBarButtonItem or rightBarButtonItem, depending on left to a newly created button.
There really isn't much way to shorten your code and still achieve the desired effect (without also obscuring the code).
You could use:
(left ? navigationItem.setLeftBarButtonItem : navigationItem.setRightBarButtonItem)(UIBarButtonItem(customView: button), animated: false)
But that's just weird.

How to check with watin is element "hidden"

Is there a way to verify is element hidden or not - using watin.
I don't want to use Jquery.
Thanks
I managed to work around this by running some jQuery in the browser that reported whether the element was hidden or not:
var jsCommand = String.Format("$('#{0}').is(':visible');", fieldId);
var isVisible = ie.Eval(jsCommand) == "true";
In my experience, there is no concrete way to tell if an individual element is hidden or not in watin.
However, you can recursively check up through the parent tree to see if they contain "display: none" or "visibility: hidden"
This blog gives more detail:
http://blog.coditate.com/2009/07/determining-html-element-visibility.html
You may want to check what method your developers use to hide elements to know if this solution is useful for you.
Not sure if this is any help, but I recently had to use a hidden text field...
public TextField HiddenTitleTextField
{
get
{
return this.Document.TextField(tf => tf.Name == "title" &&
tf.GetAttributeValue("type") == "hidden");
}
}
HTH!

Resources