Setup axeDevToolsUIKit.xcframework
Requires:
- Xcode 12.5.1 or higher
- iOS 10 or higher
- Username and Password for the axe DevTools Mobile Dashboard
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.
- In Xcode select your main project to open project settings.
- In the settings pane, ensure the main project is selected and not a target.
- Select the
Package Dependencies
tab. - Select the plus icon to add a package dependency.
- 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.
- Select Your Application's target.
- Select the General tab.
- Drag the framework to Frameworks, Libraries, and Embedded Content.
- Try building. If the error "axeDevToolsUIKit not found" appears, ensure that
- The framework file is in the correct location (project folder, designated frameworks folder, etc)
- Framework Search Path in Build Settings includes the correct path to 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)
}
}
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
}
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)
}
}