我正在为我的macOS应用程序构建UI测试。这是在macOS Sierra上使用Xcode 8。我注意到的一件事是当我记录我的UI测试时,单击我的自定义控件NSButton
子类时收到一个奇怪的错误。错误消息为:Recorder Service Error: Left Mouse Down: Failed to find matching element - Xcode error
在Google上搜索此错误时,似乎这是与AppKit中的可访问性或错误相关的问题或两者的某种组合。我使用NSView
自定义控件子类(使用mouseDown:
覆盖来模拟按钮单击)再次尝试相同的方法,UI测试能够在测试中找到并成功记录此事件。但是,使用NSButton
执行此操作时似乎存在一个非常具体的问题。
我试过的其他事情:
在身份检查器中,我确实将Identifier属性设置为特定值,但这似乎对记录我的UI测试没有任何有益影响。
我也试过覆盖:
override func accessibilityPerformPress() -> Bool {
return true
}
但由于NSButton
已符合NSAccessibilityButton
,这似乎也没有明显影响。
我还尝试在UITest中抛出断点并在Xcode控制台中通过po XCUIApplication()
打印整个XCUIApplication树,我注意到我的自定义控件似乎没有出现在树层次结构中。
以下是我的小样本应用程序中我的故事板的概述:
我已经包含了系统按钮(可以找到并正确记录)以及我下面的自定义控件NSButton。
此处还有控制台中XCUIApplication树的快照:
我的NSButton子类的代码如下所示:
@IBDesignable class CustomButton : NSButton {
//MARK: - Properties
@IBInspectable var cornerRadius : Float = 0
@IBInspectable var backgroundColor : NSColor = NSColor.clear
@IBInspectable var buttonText : String?
@IBInspectable var textColor : NSColor = NSColor.white
@IBInspectable var selectedTextColor : NSColor = NSColor.white
@IBInspectable var borderColor : NSColor = NSColor.clear
@IBInspectable var fontSize : Int = 13
@IBInspectable var borderWidth : Float = 1.5
@IBInspectable var shouldBoldText : Bool = false
@IBInspectable var shouldUnderlineText : Bool = false
fileprivate var isSetup = false
fileprivate var isSelected = false
internal var buttonTextField : NSTextField?
//MARK: - Inteface Builder Managemenet
override func prepareForInterfaceBuilder() {
configureView()
}
override func accessibilityPerformPress() -> Bool {
return true
}
//MARK: - Init
override func awakeFromNib() {
super.awakeFromNib()
configureView()
}
//MARK: - View Configuration and Styling
/// Configures the control UI elements and all styling options.
internal func configureView() {
if isSetup == false {
setAccessibilityElement(true)
isSetup = true
configureButtonTextField()
styleView()
}
}
/// Configures the button text field and applies auto layout.
fileprivate func configureButtonTextField() {
buttonTextField = NSTextField(frame: bounds)
buttonTextField!.alignment = NSTextAlignment.center
buttonTextField!.isBordered = false
buttonTextField!.isEditable = false
buttonTextField!.isSelectable = false
buttonTextField!.isBezeled = false
buttonTextField!.drawsBackground = false
buttonTextField!.translatesAutoresizingMaskIntoConstraints = false
addSubview(buttonTextField!)
//Apply the auto layout.
let horizontalXConstraint = NSLayoutConstraint(item: buttonTextField!, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: self, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0)
let verticalYConstraint = NSLayoutConstraint(item: buttonTextField!, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: self, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0)
addConstraint(horizontalXConstraint)
addConstraint(verticalYConstraint)
}
/// Applies all the user defined runtime attributes. attriutes all have
/// fallback values in case none were set in interface builder.
fileprivate func styleView() {
wantsLayer = true
layer?.backgroundColor = backgroundColor.cgColor
layer?.cornerRadius = CGFloat(cornerRadius)
layer?.masksToBounds = true
if let buttonText = buttonText {
buttonTextField?.stringValue = buttonText
}
buttonTextField?.textColor = textColor
buttonTextField?.textColor = selectedTextColor
layer?.borderColor = borderColor.cgColor
layer?.borderWidth = CGFloat(borderWidth)
if shouldBoldText == true {
buttonTextField?.font = NSFont.boldSystemFont(ofSize: CGFloat(fontSize))
} else {
buttonTextField?.font = NSFont.systemFont(ofSize: CGFloat(fontSize))
}
}
/// Updates the text color for the button text field. Updatse the selected
/// text color to match the defined color.
///
/// - parameter textColor: The color to change the button textfield text to.
internal func styleButtonText(textColor : NSColor) {
self.textColor = textColor
buttonTextField?.textColor = textColor
selectedTextColor = textColor
}
//MARK: - Click Management
override func mouseDown(with event: NSEvent) {
isSelected = true
alphaValue = 0.7
buttonTextField?.textColor = selectedTextColor
}
override func mouseUp(with event: NSEvent) {
alphaValue = 1.0
buttonTextField?.textColor = textColor
isSelected = false
guard let target = target, let action = action else {
return
}
NSApp.sendAction(action, to: target, from: self)
}
//MARK: - Cursor Rect Management
override func resetCursorRects() {
addCursorRect(bounds, cursor: NSCursor.pointingHand())
}
}
我对这个很难过。系统控件似乎在UITesting中记录并正常工作,但自定义控件(至少是NSButton)似乎会导致问题。如果有人对可能导致这种情况的任何想法有任何想法,我将不胜感激。