Setup axeDevToolsUIKit.xcframework
Requires:
- Xcode 13 or higher
- iOS 13 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
.
For automated testing or CI/CD pipelines, execute the following command as a Build Phase or as a scheme Pre-Action. open -a /Applications/Xcode.app/Contents/Applications/Accessibility\ Inspector.app/
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
axe-devtools-ios
package. - For the
Dependency Rule
, select the exact version and enter the tag to retrieve. - Click the
Add Package
button. - You'll be prompted to select a target to add the framework to. For
axeDevToolsUIKit
, select the checkbox to the left, and ensure your application's target is selected. Click theAdd Package
button, and Xcode will begin fetching the assets.
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
Add the below code to SceneDelegate
:
//In SceneDelegate.swift
import axeDevToolsUIKit
Setup the Framework
Add a variable within the SceneDelegate
object to store the instance of axe DevTools to interact with:
var axe: AxeDevTools?
Connect for a different user
If you are creating a build for another user to test with, initiate the axe login floating action button in sceneDidBecomeActive
and they will be able to authenticate with their user:
axe = AxeDevTools.showAxeFAB()
Learn more about the login feature. That's it! Upon using this build, another user will be able to use the axe floating action button to log in to the framework and start initiating their scans. Note: You may want to send them the documentation for the login feature if it's their first time with the library.
Connect with your user
If you want to get started testing with your user, initialize the framework with one of the following authentication snippets in sceneDidBecomeActive
:
Connect with an 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>")
Load the Floating Action Button (FAB)
After authenticating with your user and initializing the AxeDevTools
object, 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
Create an object within your testing class to hold onto the axe DevTools instance:
var axeDevTools: AxeDevTools?
Whether unit or UI testing, initialize the framework within the setUp
or setUpWithError
methods.
Connect with an 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>")
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 tagging 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)
}
}