Analyze Pages Using Spec Files
How to use the spec and bulk-spec subcommands to analyze pages using spec files
A spec file is a JSON or YAML file that defines a list of web pages and the browser actions to run on each page before analyzing for accessibility issues. Use axe spec to run a single spec file, or axe bulk-spec to process a directory of spec files.
- The
axe specCommand - Spec File Structure
- Actions
- Selectors
- Global Actions
- Batch Processing with
axe bulk-spec - Options
The axe spec Command
axe spec <spec-file> <output-dir> [options]The <output-dir> is where JSON results are saved. If omitted, results are saved in the current working directory.
Example usage:
axe spec ./axe-workflow.yaml ./axe-results --format htmlSpec File Structure
A spec file defines one or more projects, each with a list of pages to analyze and optional actions to perform on each page.
YAML Example
projects:
- name: deque.com
id: deque.com
metadata:
products:
- CLI
environment:
- Prod
globalActions:
- dismiss modal "#CybotCookiebotDialog" with close button "#CybotCookiebotDialogBodyButtonAccept"
pageList:
- name: Deque search
url: https://www.deque.com/
actions:
- type "axe" into element "#searchform input"
- click element "#searchform button"
- wait for element ".m-search-page" to be found
- analyze
- name: Axe Dashboard
url: https://axe.deque.com/Project
| Property | Type | Description |
|---|---|---|
name |
string | Unique display name of the project. |
id |
string | Unique identifier of the project. |
metadata |
object | Optional. Arbitrary metadata for your use case (e.g., product name, environment). |
globalActions |
array | Optional. Actions that respond to state changes on every page in the project, for example, dismissing a cookie banner or survey pop-up. See Global Actions. |
pageList |
array | List of pages to analyze. See Page. |
Page
| Property | Type | Description |
|---|---|---|
name |
string | Display name of the page. |
url |
string | URL of the page. |
actions |
array | Optional. Actions to perform on the page before or after analysis. See Actions. |
JSON/YAML Format
Spec files can be written in YAML or JSON. The following table shows the same values in each format. Note that in JSON, action strings that contain double quotes must escape them with a backslash.
| YAML | JSON |
|---|---|
type "axe" into element "#searchform input" |
"type \"axe\" into element \"#searchform input\"" |
dismiss modal "#CybotCookiebotDialog" with close button "#CybotCookiebotDialogBodyButtonAccept" |
"dismiss modal \"#CybotCookiebotDialog\" with close button \"#CybotCookiebotDialogBodyButtonAccept\"" |
Actions
Actions are strings in the actions (or globalActions) array of a spec file. They perform tasks like clicking buttons, filling forms, dismissing dialogs, waiting for page states, and running accessibility analyses. Actions run in the order listed.
There are two types of actions:
- Page actions run in sequence on a specific page. The
analyzeaction must be called at least once per page. - Global actions run on every page in the project in response to state changes. See Global Actions.
Complete Action Example
The following example logs into Deque University and analyzes the dashboard:
projects:
- name: Deque University login flow
id: deque-university-login-flow
pageList:
- name: homepage
url: https://dequeuniversity.com/
actions:
- click element ".loginLink"
- wait for element ".loginUsername" to be found
- type "user@example.com" into element ".loginUsername"
- type "secretpassword" into element "#loginPassword"
- click element "input[type=submit]"
- wait for element ".logoutLink" to be found
- analyze pageSelectors
Many actions take a selector argument that identifies an element on the page. A selector can be a CSS selector or XPath selector, specified as a single string or a list of strings.
To target elements inside iframes, you must use a list. All selectors in the list except the last one identify successive <iframe> elements to navigate into, and must be CSS selectors. The last selector in the list identifies the target element and can be CSS or XPath. Each selector in the list is evaluated relative to the document context established by the previous entry: the first selector is relative to the root document, and each subsequent iframe selector is relative to the document inside the preceding iframe.
Using a single string (not a list) cannot navigate into iframes.
Iframe Selector Example
Consider this HTML structure:
<body>
<!-- root document -->
<iframe class="payment-widget">
<!-- document inside the payment-widget iframe -->
<div class="form-wrapper">
<iframe id="card-fields">
<!-- document inside the card-fields iframe -->
<form>
<input type="text" name="card-number" class="card-input">
</form>
</iframe>
</div>
</iframe>
</body>To click the card number input, use a selector list. Each CSS selector is evaluated within the document context established by the previous entry:
# "iframe.payment-widget" is evaluated in the root document
# "#card-fields" is evaluated in the document inside iframe.payment-widget
# ".card-input" is evaluated in the document inside #card-fields
click element [ "iframe.payment-widget", "#card-fields", ".card-input" ]To use XPath for the final target element (the iframe selectors must still be CSS):
click element [ "iframe.payment-widget", "#card-fields", "//input[@name='card-number']" ]In JSON:
"click element [\"iframe.payment-widget\", \"#card-fields\", \".card-input\"]"Page Actions
The CLI supports nine page actions:
analyze: run an accessibility analysischange: change the value of an<input>,<textarea>, or<select>via JavaScriptclick: click an elementdismiss: dismiss a popup or modaleval: evaluate arbitrary JavaScriptpress: press a key (with or without modifiers)select: select an option in a<select>type: type into an<input>wait: wait for a specific state or sleep
analyze
The analyze action runs an accessibility analysis. It must be called at least once per page. You may call it multiple times to analyze a page at different points in a workflow (use the with title variant to distinguish results).
The optional ruleset parameter specifies which ruleset to use. The default is WCAG 2.1 AA. Available rulesets:
| Ruleset ID | Standard |
|---|---|
wcag2 |
WCAG 2.0 AA |
wcag2.1 |
WCAG 2.1 AA (default) |
wcag2.2 |
WCAG 2.2 AA |
wcag2aaa |
WCAG 2.0 AAA |
wcag2.1aaa |
WCAG 2.1 AAA |
wcag2.2aaa |
WCAG 2.2 AAA |
508 |
Section 508 |
ttv5 |
Trusted Tester v5 |
en301549 |
EN 301 549 |
rgaav4 |
RGAA v4 |
For information on including or excluding elements, see the axe-core API documentation on the Context parameter.
# Analyze using the WCAG 2.1 AA ruleset (default) — all three forms are equivalent
analyze
analyze the page
analyze page
# Analyze using the Section 508 ruleset
analyze page with ruleset "508"
# Analyze with a custom title (useful when analyzing a page multiple times)
analyze the page with title "after login"
# Analyze only a specific element
analyze only element "#main-content"
# Analyze only specific elements
analyze only element "#idOfElement" and element ".classToAnalyze"
# Analyze everything except images that are immediate children of paragraphs
analyze the page excluding element "p > img"
# Analyze everything except elements inside a frame with a specific class
analyze the page excluding element [ ".classOfFrameToExclude", "#idOfElement" ]
# Save results to a specific directory
analyze the page and save in "./homepage-team/"
# Save a copy to an additional directory while also saving to the default location
analyze the page and save a copy in "./homepage-team/"
# Use the axe-core library's built-in default ruleset
analyze the page with the source default rulesetExample combining multiple options: Analyze only images within elements with the class third-party and forms that aren't validated on submission, excluding elements with the class old-api, using the 508 ruleset, with a custom title and a custom save location.
analyze only element [ ".third-party", "img"] and element "form[novalidate]" excluding element ".old-api" with ruleset "508" with title "What is this testing" and save in "Results for Some test"In JSON:
"analyze only element [\".third-party\", \"img\"] and element \"form[novalidate]\" excluding element \".old-api\" with ruleset \"508\" with title \"What is this testing\" and save in \"Results for Some test\""change
The change action changes the value of an <input>, <textarea>, or <select> element via JavaScript. Use change when normal DOM events are not available.
# Change the value of an input
change the value of "input[name=song]" to "too many puppies"click
The click action clicks the first element that matches the given selector.
# Click a button by class selector
click element ".myButton"
# Click the body element
click "body"dismiss
The dismiss action closes a popup or modal by clicking its close button. Provide a CSS selector for the modal container and another for the close button. The action fails gracefully if either element is not present.
This action does not dismiss native alert() or confirm() dialogs.
# Dismiss a modal using separate selectors for the container and close button
dismiss modal ".myModal" with close button ".myModal .close"eval
The eval action executes arbitrary JavaScript on the page. Use it to manipulate the DOM or perform custom actions.
# Change the page title
eval "document.title = 'hello world'"
# Scroll an element into view
eval "document.querySelector('.someElement').scrollIntoView()"
# Scroll to the bottom of the page
eval "window.scrollTo(0, document.body.scrollHeight)"press
The press action sends a key press to an element (optionally with modifier keys). For supported key names, see the Selenium key documentation.
# Press H on the body element
press "H" on "body"
# Press Shift+Tab on the navigation element
press "shift+tab" on element ".navigation"
# Press Shift+Control+7 on an element
press "shift+control+7" on element ".foo"select
The select action selects an <option> of a <select> element by its visible text (not its value attribute).
Given this HTML:
<select class="mySelect">
<option></option>
<option value="1">dog</option>
<option value="2">cat</option>
<option value="3">fish</option>
</select># Select by visible option text
select the "dog" option in ".mySelect"
select the "cat" option in element ".mySelect"type
The type action types a string into an <input> or <textarea> element. Use it to fill out forms and populate search fields.
# Type into an email input
type "user@example.com" into element "input[type=email]"
# Type into a textarea
type "hello world" into "textarea.Message"
# Type with a delay between keystrokes to simulate human typing
type "sloth" into "input[type=search]" with a 150ms key delaywait
The wait action either waits for an element to reach a specified state, or sleeps for a given duration.
Supported element states: visible, hidden, selected, enabled, disabled, found.
For sleep duration, numeric values are interpreted as milliseconds. Strings are converted using the ms package — for example, 1m = 60,000 ms, 1s = 1,000 ms.
# Wait for an element to appear
wait for element ".myElement" to be found
# Wait for an element to become hidden
wait for element ".myElement" to be hidden
# Sleep for 1 minute
wait for 1m
# Sleep for 1 second
wait for 1s
# Sleep for 30 milliseconds
wait for 30Global Actions
Global actions run on every page in a project in response to state changes, rather than running procedurally like page actions. Only one global action is currently supported: dismiss modal.
- Global actions work in both
specmode and headless URI mode. - Global actions are not procedural — they trigger in response to page events, not in a fixed sequence.
- The
dismiss modalglobal action waits for a specified modal to appear and dismisses it before page actions continue.
Add global actions to a project after name/id and before pageList:
projects:
- id: demo
name: CLI demo
globalActions:
- dismiss modal "#__next .survey" with close button ".survey button.close"
pageList:
- name: homepage
url: https://dequelabs.github.io/aget-demo-site
- name: popup
url: https://dequelabs.github.io/aget-demo-site
actions:
- wait for element "#__next header nav" to be visible
- click element "#__next header nav a[href*=popup]"
- wait for element ".content button" to be found
- analyze with title "before popup"
- click element ".content button"
- analyze with title "with popup"
- dismiss modal ".ReactModal__Content" with close button ".ReactModal__Content .close"
- analyze with title "after popup"
- name: contact
url: https://dequelabs.github.io/aget-demo-site/contact
actions:
- analyze with title "form disabled"
- wait for element "#__next .toggle" to be found
- click element ".toggle button"
- wait for element "input[name=name]" to be enabled
- analyze with title "form enabled"
- type "stephen" into element "input[name=name]"
- type "555-555-5555" into element "input[name=phone]"
- type "stephen@deque.com" into element "input[name=email]"
- type "hello world" into element "textarea[name=message]"
- click element "button[type=submit]"
- wait for element ".thanks" to be found
- analyze with title "thanks message"Batch Processing with axe bulk-spec
To process multiple spec files in a single run, use axe bulk-spec with a directory containing spec files. The CLI recursively searches the directory and its subdirectories for spec files.
axe bulk-spec <spec-files-directory> <output-directory>The <output-directory> is optional — if omitted, results are saved in the current working directory.
Progress updates are printed to stdout during the run.
Results are written to the output directory: one JSON file per analyze action, plus a log file listing any spec files that failed and the reason for failure.
Options
The following options are available for axe spec:
--axe-devhub-api-key <api-key>
Specifies the Axe Developer Hub API key. Required (along with --axe-devhub-project-id) to send results to Axe Developer Hub. See Send Results to Axe Developer Hub.
--axe-devhub-project-id <project-id>
Specifies the Axe Developer Hub project ID. Required (along with --axe-devhub-api-key) to send results to Axe Developer Hub. See Send Results to Axe Developer Hub.
--axe-devhub-server-url <url>
Specifies the URL of the Axe Developer Hub server. Defaults to https://axe.deque.com. Equivalent to the AXE_DEVHUB_SERVER_URL environment variable. See Send Results to Axe Developer Hub.
-c, --custom <path>
Specifies a custom ruleset file, overriding the default ruleset.
--descendant-links
Collects the links on each page and appends them to the results. Requires --verbose.
--dismiss-alerts
Automatically dismisses browser alert(), confirm(), and prompt() dialogs before scanning.
--enable-tracking <state>
Enables sending data to the metrics library.
--filter <type(s)>
Filters result types from output: passes, violations, incomplete, inapplicable. Requires --format csv.
-f, --format <type(s)>
Report format(s): html, junit, csv, or a comma-separated combination. Default: html.
--no-git-data
Excludes Git branch and commit information when sending results to Axe Developer Hub. See Send Results to Axe Developer Hub.
--page-name <name>
Runs only the page with the specified name from the spec file's pageList.
--page-source
Appends the scanned HTML source to results. Requires --verbose.
--page-title
Appends the page title to results. Requires --verbose.
--remote-proxy <proxy-server>
Routes traffic through the specified remote proxy server.
--resume-from <name>
Skips all pages before the named page in the spec file's pageList.
--scanned-url
Adds the base URL and current scan URL to verbose results. Chrome only. Requires --verbose.
--set-distinct-id <id>
Overrides the distinct ID value.
--set-tracking-url <url>
Overrides the URL where metrics data is sent.
-t, --tags
Filters the standard ruleset by tag.
--user-agent
Sets a custom user agent string for the browser.
--validate
Validates your spec file without running it.
-v, --verbose
Includes additional output: Axe results and metadata such as tool name, version, and environment.
For additional configuration options, see Configure.
