Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 54 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,77 @@
# Android Sample App for axe DevTools Mobile by Deque

A sample application built solely to showcase axe DevTools Android automated espresso test implementation. It is non-functional and made inaccessibly by design.
A sample application built solely to showcase axe DevTools Android automated espresso test implementation. It is non-functional and made inaccessible by design.

## Helpful Links
- Library documentation and more information on automated or manual testing can be found at [docs.deque.com](https://docs.deque.com/devtools-mobile/).
- Learn more about [Deque and axe DevTools Mobile here](https://www.deque.com/).

------

## Getting Started:
## Getting Started
We currently support two ways to scan your app for accessibility issues, both delivered through our Gradle Plugin.

1. Clone the repository
#### Auto Scan
Users looking to keep the amount of code needed to implement our library to a minimum should look at
Auto Scan. Enabling it lets our Gradle Plugin take on the boilerplate for you. With a few lines of
configuration in your `build.gradle` file, a report is generated for you that covers the accessibility
violations in your app, with no test code required. We prefer this implementation in our own use of this library, so please try it out!

#### Targeted Scan
For those who would like more control over when and what gets scanned, you can run targeted scans by
calling `axe.scan()` directly in your tests. This lets you scan a specific screen or state exactly when
you want to.

### Setting Up Auto Scan
1. Clone the repository.
2. Set up a new project on [axe Developer Hub](https://axe.deque.com/axe-watcher) to set up a Project ID and API Key.
- Or Grab an API key from the [settings page](https://axe.deque.com/settings)
3. In `app/build.gradle`, add your API Key in the `AXE_DEVTOOLS_APIKEY` variable
4. If you set up a project add your Project ID in the `app/build.gradle` under the `AXE_DEVTOOLS_PROJECT_ID` variable
3. In `app/build.gradle`, add `id 'com.deque.android' version '1.+'` to your plugins block, then add your API key and project ID to the axeDevTools block and set `axeAutoScanMode = true` as shown in the example below.

```groovy
android {
def AXE_DEVTOOLS_APIKEY = "YOUR_API_KEY_HERE"
def AXE_DEVTOOLS_PROJECT_ID = "AXE_PROJECT_ID_HERE"
axeDevTools {
axeMobileApiKey = axe_apikey
axeProjectId = axe_project_id
axeAutoScanMode = true
}
```
Comment thread
pat-deque marked this conversation as resolved.
If you would like to customize the implementation of this further the following example shows all the configuration options we have available to you:
```groovy
axeDevTools {
axeMobileApiKey = axe_apikey
axeProjectId = axe_project_id
axeAutoScanMode = true // to enable auto scan mode (default false)
axeUploadResults = false // if you want to only look at results locally (default true)
axeAccountUrl = "https://www.custom-url.com" // found in your project settings on axe Developer Hub (default https://axe.deque.com)
axeHtmlReportPath = "/path/to/desired/folder" // customize where your accessibility reports are saved (defaults to your project's build/reports directory)
}
Comment thread
pat-deque marked this conversation as resolved.
```
### Setting Up Targeted Scan
Targeted scans use the same Gradle Plugin, but with Auto Scan turned off so you control when scans run. Call `axe.scan()` in your tests wherever you want to capture results — see `InstrumentationRegistryExampleTest` and `AxeTestClass` for a working example.

1. Clone the repository.
2. Set up a new project on [axe Developer Hub](https://axe.deque.com/axe-watcher) to set up a Project ID and API Key.
3. In `app/build.gradle`, add `id 'com.deque.android' version '1.+'` to your plugins block and add your API key and project ID to the axeDevTools block (leave `axeAutoScanMode` omitted or set to `false`):

```groovy
axeDevTools {
axeMobileApiKey = axe_apikey
axeProjectId = axe_project_id
}
```
Comment thread
pat-deque marked this conversation as resolved.
4. Call `axe.scan()` in your test code where you want to run a scan, as shown in `AxeTestClass`.

> **Prefer to manage the dependency yourself?** Instead of applying the Gradle Plugin, you can add our base library directly with `androidTestImplementation 'com.deque.android:axe-devtools-android:current-release'` in your `dependencies` block. Note that Auto Scan is not available when using this approach.
## Running the Tests

Once you add your variables you are ready to start scanning the application using the espresso tests.
Once you have set up either Auto Scan or a Targeted Scan, you are ready to start scanning the application using the Espresso tests.

You can see accessibility testing in action through the `ExampleEndToEndAccessibilityTest` Espresso test or any other test in the `androidTest` folder.
The `androidTest` folder contains examples of `Jeptpack Compose`, `XML` and `UiAutomator`.
You can see accessibility testing in action through the `AutoScanDemoTest` and `InstrumentationRegistryExampleTest` tests, or any other test in the `androidTest` folder.
These include examples using `Jetpack Compose` and `UiAutomator`.
You can kick it off from Android Studio, or through an automated service such as Perfecto, Sauce Labs, etc.

_Note:_
- This project is set to use the Android Gradle plugin version 8.11.2. Running the project with newer, or older, JDK versions may require you to update the Gradle plugin version accordingly. Please refer to [Android's Updating the Gradle Plugin](https://developer.android.com/studio/releases/gradle-plugin#updating-plugin) documentation for more.
- This project is set to use the Android Gradle plugin version 8.10.1. Running the project with newer, or older, JDK versions may require you to update the Gradle plugin version accordingly. Please refer to [Android's Updating the Gradle Plugin](https://developer.android.com/studio/releases/gradle-plugin#updating-plugin) documentation for more.


### Perfecto
Expand Down
14 changes: 9 additions & 5 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ plugins {
id 'com.perfectomobile.instrumentedtest.gradleplugin'
id 'org.jetbrains.kotlin.plugin.serialization' version '2.2.20'
id("org.jetbrains.kotlin.plugin.compose") version "2.2.20"
id 'com.deque.android' version '1.1.0'
}

axeDevTools {
axeMobileApiKey = project.findProperty('axe_apikey') ? axe_apikey : ""
axeProjectId = project.findProperty('axe_project_id') ? axe_project_id : ""
axeAutoScanMode = true
axeAccountUrl = "https://axe.deque.com"
axeUploadResults = false
}
Comment thread
pat-deque marked this conversation as resolved.

android {
Expand Down Expand Up @@ -157,11 +166,6 @@ dependencies {
implementation 'com.google.android.material:material:1.10.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'

androidTestImplementation 'com.deque.android:axe-devtools-android:8.3.0'

// MLKit implementation for use with ADT Library
implementation 'com.google.mlkit:text-recognition:16.0.1'

implementation 'androidx.activity:activity-compose:1.8.0'
implementation platform('androidx.compose:compose-bom:2023.03.00')
implementation 'androidx.compose.ui:ui'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.deque.mobile.axedevtoolssampleapp

import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

/**
* Auto Scan Sample Test Suite:
*
* The purpose of this test class is to provide a sample test suite that emulates what a typical user
* experience might be like while using our Auto Scan feature.
*
* Please follow the README to ensure that you have properly set up the build.gradle file for this feature.
* If the project is properly set up for Auto Scan you should see an HTML report show up in your build/reports
* directory as well as a result uploaded to your scan result dashboard.
*/
@RunWith(AndroidJUnit4::class)
class AutoScanDemoTest {

@get:Rule
val composeTestRule = createAndroidComposeRule<MainActivity>()

private fun openTab(menuItemId: Int) = onView(withId(menuItemId)).perform(click())

private fun getString(resId: Int): String =
composeTestRule.activity.getString(resId)

@Test
fun catalogTabIsShownOnLaunch() {
// Catalog is the start destination, so its heading should be visible immediately.
onView(withId(R.id.catalog_heading))
.check(matches(isDisplayed()))
.check(matches(withText(R.string.catalog)))
}

@Test
fun navigatingToMenuShowsComposeContent() {
openTab(R.id.menu)

// The Menu screen is rendered with Compose, so assert against the Compose tree.
composeTestRule.waitForIdle()
composeTestRule.onNodeWithText(getString(R.string.customer)).assertIsDisplayed()
composeTestRule.onNodeWithText(getString(R.string.james_anderson)).assertIsDisplayed()
composeTestRule.onNodeWithText(getString(R.string.log_out)).assertIsDisplayed()
}

@Test
fun tabSelectionStateIsRestoredWhenReturningToCatalog() {
openTab(R.id.catalog)
onView(withId(R.id.catalog_heading))
.check(matches(isDisplayed()))
.check(matches(withText(R.string.catalog)))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.deque.mobile.axedevtoolssampleapp

import com.deque.mobile.axedevtoolssampleapp.test.BuildConfig
import com.deque.mobile.devtools.AxeDevTools
import org.junit.AfterClass

abstract class AxeTestClass {

init {
/**
* Start a session on axe Developer Hub with a valid API key and your project ID. If
* a session has already been started for this test suite, that session will be used instead
* of creating a new session.
*
* You can find and create projects here: https://axe.deque.com/axe-watcher
*/
Comment thread
pat-deque marked this conversation as resolved.

axe.startScanSession(
apiKey = BuildConfig.AXE_DEVTOOLS_APIKEY,
projectId = BuildConfig.AXE_DEVTOOLS_PROJECT_ID
)
}

companion object {
val axe = AxeDevTools()

@JvmStatic
@AfterClass
fun afterClass() {
// Generates a report of all scans in a given test class to a report that
// tells you which accessibility violations we found.
axe.generateHtmlReportAndSummary("axe")

axe.tearDown()
}
Comment thread
Copilot marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,29 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.deque.mobile.axedevtoolssampleapp.test.BuildConfig
import com.deque.mobile.devtools.AxeDevTools
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test

class InstrumentationRegistryExampleTest {
/**
* Instrumentation Registry Sample Test Suite:
*
* The purpose of this test class is to provide a sample test suite that emulates what a typical user
* experience might be like while running a targeted scan via the InstrumentationRegistry.
*
* Please follow the README to ensure that you have properly set up the build.gradle file for this feature.
* If the project is properly set up you should see an HTML report show up in your build/reports
* directory as well as a result uploaded to your scan result dashboard.
*
* Please also check out the AxeTestClass for an example of how to set up our main interface.
*/

class InstrumentationRegistryExampleTest : AxeTestClass() {

@Rule
@JvmField
val rule: ActivityScenarioRule<MainActivity> = ActivityScenarioRule(MainActivity::class.java)


private val axe = AxeDevTools()

init {
/**
* Start a session on axe Developer Hub with a valid API key and your project ID.
*
* You can find and create a projects here : https://axe.deque.com/axe-watcher
*/

axe.startSession(
apiKey = BuildConfig.AXE_DEVTOOLS_APIKEY,
projectId = BuildConfig.AXE_DEVTOOLS_PROJECT_ID
)
}

@Before
fun setupAxeDevTools() {
val instrumentation = InstrumentationRegistry.getInstrumentation()
Expand Down Expand Up @@ -75,12 +69,7 @@ class InstrumentationRegistryExampleTest {
// }
// assertEquals(6, fails)

// 3. Save the result JSON to a local file for later use
// 3. Save the AxeResult JSON to a local file for later use
scanResultHandler?.saveResultToLocalStorage("axe")
}

@After
fun tearDown() {
axe.tearDown()
}
}
Loading