GithubHelp home page GithubHelp logo

kmrax / accessibilityidentificable Goto Github PK

View Code? Open in Web Editor NEW

This project forked from m1entus/accessibilityidentificable

0.0 0.0 0.0 22 KB

Identifiers for UI testing: a reflection based approach

License: MIT License

Ruby 0.77% Swift 99.23%

accessibilityidentificable's Introduction

AccessibilityIdentificable

Identifiers for UI testing: a reflection based approach

One of the most annoying things about UI Testing in iOS is the need to assign Accessibility Identifiers to views that are hard to access otherwise.

What if we could generate and assign Accessibility Identifiers automatically?

Usually, assigning an Accessibility Identifier to a View is rather straightforward and it is creating a lot of boilerplate code…

override func viewDidLoad() {
    titleLabel.accessibilityIdentifier          = "SomethingViewController.titleLabel"
    descriptionLabel.accessibilityIdentifier    = "SomethingViewController.descriptionLabel"
    doneButton.accessibilityIdentifier          = "SomethingViewController.doneButton"
}

I was searching for some solution but they were not fully satisfy me, but i used them to create this one. I have used Swift’s reflection API: Mirror with Objective-C swizzling technique and everything is completed by the code generator Sourcery to have direct reference from UITests.

@objc public protocol AccessibilityIdentificable { /* NOOP */ }

public extension AccessibilityIdentificable {

    func generateAccessibilityIdentifiers() {
        let mirror = Mirror(reflecting: self)

        for child in mirror.children {
            guard let identifier = child.label?.replacingOccurrences(of: ".storage", with: "")
                    .replacingOccurrences(of: "$__lazy_storage_$_", with: "") else {
                continue
            }

            if let view = child.value as? UIView {
                view.accessibilityIdentifier = "\(type(of: self)).\(identifier)"

            } else if let array = child.value as? [UIView] {
                for (index, object) in array.enumerated() {
                    object.accessibilityIdentifier = "\(type(of: self)).\(identifier)_\(index)"
                }
            }
        }
    }
}

Usage is straightforward, all properties from classes that implements AccessibilityIdentificable protocol will have generated accessibility identifiers. Just implement AccessibilityIdentificable to UIViewController or custom object and add AccessibilityInjector.inject() line for swizzling UIViewController viewDidLoad, init(frame:) and init(coder:) of UIView. We need that to call automatically generateAccessibilityIdentifiers from AccessibilityIdentificable after these methods and assign accessibilityIdentifier to each UIView.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

    if ProcessInfo.processInfo.arguments.contains("-xcuitest") {
        AccessibilityInjector.inject()
    }
    return true
}
class CustomView: UIView, AccessibilityIdentificable {
    private var label: UILabel = UILabel(frame: .zero) #--> will set accessibilityIdentifier: CustomView.label
}

To be able to find and generate all classes that implements AccessibilityIdentificable you need to add Build Phases -> Run Script which will change depends on project structure and for me look like this:

#/bin/sh

$PODS_ROOT/Sourcery/bin/sourcery --sources $SRCROOT --sources $SRCROOT/../Source --output $SRCROOT/AccessibilityIdentificableUITests/Generated --templates $SRCROOT/../Template

Sourcery in UITests will generate classes:

// @generated
// MARK: - Identifiers > CustomView

public extension Identifiers {
    struct CustomView {
        public static var label = "CustomView.label"
    }
}

Then inside UITests you can use this identifiers:

func testExample() throws {
    // UI tests must launch the application that they test.
    let app = XCUIApplication()
    app.launchArguments.append("-xcuitest")
    app.launch()

    XCTAssert(app.staticTexts[Identifiers.CustomView.label].exists)
}

In addition to that if you would like to add custom accessibility identifiers for some reasons, i prepared @propertyWrapper named @AccessibilityIdentify which you can use setting custom identifiers.

class ViewController: UIViewController {

    @AccessibilityIdentify(identifier: "custom_id")
    var testView = UIView()
}

accessibilityidentificable's People

Contributors

m1entus avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.