Setup axeDevToolsUIKit.xcframework

Link to Setup axeDevToolsUIKit.xcframework copied to clipboard

Requires:

Load the Accessibility Layer

For simulators, open the Accessibility Inspector by right-clicking on the Xcode icon and navigating to Open Developer Tool > Accessibility Inspector

For devices, turn on VoiceOver (alternatively a switch control) by navigating to Settings > Accessibility > VoiceOver > On

Download & Embed Framework

Using Swift Package Manager

Package URL: https://github.com/dequelabs/axe-devtools-ios.

  1. In Xcode select your main project to open project settings.
  2. In the settings pane, ensure the main project is selected and not a target.
  3. Select the Package Dependencies tab.
  4. Select the plus icon to add a package dependency.
  5. Insert the package URL and select the version or branch to pull from.

Using Artifactory

View the instructions for using artifactory.

Embed Framework

Now that you've downloaded the framework, it's time to embed it into your application.

  1. Select Your Application's target.
  2. Select the General tab.
  3. Drag the framework to Frameworks, Libraries, and Embedded Content.
  4. Try building. If the error "axeDevToolsUIKit not found" appears, ensure that
    1. The framework file is in the correct location (project folder, designated frameworks folder, etc)
    2. Framework Search Path in Build Settings includes the correct path to the framework


screenshot of Xcode highlighting steps to embed the framework.

Manual Testing

Note: The below snippets are for iOS 13 and higher. Prior to iOS 13, include the below snippets in the AppDelegate's applicationDidBecomeActive and applicationWillResignActive

Add the below code to SceneDelegate:

//In SceneDelegate.swift
import axeDevToolsUIKit

Initialize the Framework

Add a variable within the SceneDelegate object to store the instance of axe DevTools to interact with:

var axe: AxeDevTools?

In sceneDidBecomeActive place one of the following authentication snippets to authenticate with our framework.

Connect with API key:

Generate an API key at axe.deque.com.

axe = try? AxeDevTools.login(withAPIKey: "<DEQUE_APIKEY>")

Connect with username and password:

axe = try? AxeDevTools.login(withUsername: "<DEQUE_USERNAME>", andPassword: "<DEQUE_PASSWORD>")

Connect to the desktop application:

To get started with the desktop application, be sure to reference Desktop Setup.

axe = try? AxeDevTools.setLocalConnection()
Enable local networking and arbitrary loads within your `Info.plist`


        <key>NSAppTransportSecurity</key>
        <dict>
            <key>NSAllowsArbitraryLoads</key>
            <true/>
            <key>NSAllowsLocalNetworking</key>
            <true/>
        </dict>

Load the Floating Action Button (FAB)

After setting the server in sceneDidBecomeActive, start the floating action button for manual testing:

axe?.showA11yFAB()

In sceneWillResignActive stop testing and remove the floating action button.

axe?.hideA11yFAB()

From here, you can now run your application to access the floating action button. The button can be moved around the screen to not obstruct development. When you're ready for a scan, tap the button and a scan of the current screen will be sent to axe DevTools Mobile Dashboard where you can view the results.

Automated Testing

This framework works with many iOS testing frameworks, but we'll use XCTest in the below examples. Be sure to check out Advanced Automation for other testing framework examples.

Setup for Testing

In any file used for accessibility testing, import the framework.

import axeDevToolsUIKit

Whether unit or UI testing, initialize the framework within the setUp or setUpWithError methods.

Connect with API key:

Generate an API key at axe.deque.com.

axe = try? AxeDevTools.login(withAPIKey: "<DEQUE_APIKEY>")

Connect with username and password:

axe = try? AxeDevTools.login(withUsername: "<DEQUE_USERNAME>", andPassword: "<DEQUE_PASSWORD>")

Connect to the desktop application:

To get started with the desktop application, be sure to reference Desktop Setup.

axe = try? AxeDevTools.setLocalConnection()
Enable local networking and arbitrary loads within your `Info.plist`


        <key>NSAppTransportSecurity</key>
        <dict>
            <key>NSAllowsArbitraryLoads</key>
            <true/>
            <key>NSAllowsLocalNetworking</key>
            <true/>
        </dict>

Setup Example

var axe: AxeDevTools?

override func setUpWithError() throws {
    axe = try AxeDevTools.login(withAPIKey: "<DEQUE_APIKEY>")
}

Unit Testing

To begin testing, there are a few different APIs you can call based on how your application is developed. Any view controller or view that is passed in is assumed to be the parent view and will automatically include the child views in the scan.

To test the initial view of a storyboard:

let result = try? axe?.run(onStoryboardName: "Main")

To test a view controller:

let result = try? axe?.run(onViewController: UIViewController)

To test a view:

let result = try? axe?.run(onView: UIView)

Send Results to the Dashboard

Utilize postResult() from the axe DevTools object in any of the above examples to send the result of the scan to the server.

import axeDevToolsUIKit

class UnitTests: XCTestCase {
    var axe: AxeDevTools?

    override func setUpWithError() throws {
        axe = try AxeDevTools.login(withAPIKey: "<DEQUE_APIKEY>")
    }

    func testAccessibility() throws {
        let view = CustomView()
        let result = try axe?.run(onView: view)
        try axe?.postResult(result)
    }
}
tip

You can use the axe DevTools object for other great features as well, such as saving results locally on your machine and tag results in the dashboard.

UI Testing

To switch from manual testing to automated UI testing, a small change to the setup code you added in Interactive Testing is required. As the floating action button is still required, we'll need to change axe?.showA11yFAB() to use the automated instance instead. The automated floating action button is invisible to not interfere with any screenshot testing you have, but is still programmatically tappable to capture a scan.

After setting the server in sceneDidBecomeActive, replace the call to axe?.showA11yFAB() with the below snippet for automated testing:

axe?.showA11yFAB(customFAB: AutomatedTestFAB())

Setup for Testing

In your UI test file, import the framework.

import axeDevToolsUIKit

As you won't have access to your application's axe DevTools object, you can utilize the setUp method to have one available for your tests by logging in again. Use this object to interact with available features.

import axeDevToolsUIKit
import XCTest

class MyUITests: XCTestCase {
    var axe: AxeDevTools?
    var app = XCUIApplication()

    override func setUpWithError() throws {
        axe = try AxeDevTools.login(withAPIKey: "<DEQUE_APIKEY>")

        app.launch()
    }
}

Capture a Scan

When you're ready to capture a scan, use the floating action button's (FAB) accessibility identifier "com.deque.axeDevTools.accessibilityFab" to programmatically tap it and send a scan to the dashboard:

XCUIApplication().buttons["com.deque.axeDevTools.accessibilityFab"].tap()

Wait for the FAB to return to know it has finished uploading. The FAB's label will now hold a key you can use to retrieve the scan from the server.

XCTAssert(fab.waitForExistence(timeout: 5))

// Grab the result from the server
guard let key = AxeDevToolsResultKey(fabTitle: fab.label) else {
    XCTFail("Could not retrieve scan from the server.")
    return
}
tip

The result key looks like this:

{
  "userId": "55555555-5555-5555-5555-555555555555",
  "packageName": "com.example.app",
  "resultId": "123456789ab"
}

With the key, you can now get additional information from the scan such as passes and failures, and update the scan with any tags you want to include.

// Tag the result with the tags provided
try? axe?.tagResult(key, withTags: tags)

Full Example


import axe_devtools_ios_sample_app
import axeDevToolsUIKit
import XCTest

class MyAppUITests: XCTestCase {
    var axe: AxeDevTools?
    var app = XCUIApplication()

    override func setUpWithError() throws {
        axe = try AxeDevTools.login(withAPIKey: "<DEQUE_APIKEY>")

        app.launch()
    }

    func testAccessibility() throws {
        let FAB_ID = "com.deque.axeDevTools.accessibilityFab"
        let fab = app.buttons[FAB_ID]

        // Tap the FAB to send the scan to the server
        fab.tap()

        // Once it returns, the result from the scan can be seen on the Dashboard.
        // The FAB's label has the key needed to retrieve the scan from the server.
        XCTAssert(fab.waitForExistence(timeout: 5))

        // Grab the result from the server
        guard let key = AxeDevToolsResultKey(fabTitle: fab.label) else {
            XCTFail("Could not retrieve scan from the server.")
            return
        }

        // Tag the result with the tags provided
        try axe?.tagResult(key, withTags: ["sauce testing"])

        guard let result = try axe?.getResult(key) else {
            XCTFail("Not logged in. Result could not be fetched.")
            return
        }

        // Fail the build if accessibility issues were found
        XCTAssertEqual(result.failures.count, 0)
    }
}