├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── general_question.md ├── dependabot.yml ├── policies │ └── resourceManagement.yml └── pull_request_template.md ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── csharp-selenium-webdriver-sample ├── .gitignore ├── CSharpSeleniumWebdriverSample.csproj ├── README.md ├── Resources │ ├── screenshot-dotnet-test-failure.png │ ├── screenshot-dotnet-test-success.png │ ├── screenshot-logs-tab.png │ └── screenshot-tests-tab.png ├── SamplePage.html ├── SamplePageTests.cs ├── WebDriverFactory.cs └── azure-pipelines.yml ├── typescript-playwright-sample ├── .gitignore ├── README.md ├── assets │ ├── screenshot-failing-logs-tab.png │ ├── screenshot-failing-scans-tab.png │ ├── screenshot-failing-tests-tab.png │ ├── screenshot-local-yarn-test.png │ ├── screenshot-passing-logs-tab.png │ ├── screenshot-passing-scans-tab.png │ └── screenshot-passing-tests-tab.png ├── azure-pipelines.yml ├── package.json ├── playwright.config.ts ├── src │ └── index.html ├── tests │ ├── export-to-sarif.ts │ ├── failing-examples.spec.ts │ └── passing-examples.spec.ts └── yarn.lock ├── typescript-selenium-webdriver-sample ├── .gitignore ├── .vscode │ └── launch.json ├── README.md ├── assets │ ├── screenshot-logs-tab.png │ ├── screenshot-scans-tab.png │ ├── screenshot-tests-tab.png │ └── screenshot-yarn-test-success.png ├── azure-pipelines.yml ├── jest.config.js ├── package.json ├── src │ └── index.html ├── tests │ ├── __snapshots__ │ │ └── index.test.ts.snap │ ├── index.test.ts │ └── webdriver-factory.ts ├── tsconfig.json └── yarn.lock └── windows-app-resources └── README.md /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # default owners for everything in the repository. 2 | * @microsoft/accessibility-insights-code-owners 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug, needs triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe the bug 11 | 12 | 13 | 14 | **To Reproduce** 15 | Steps to reproduce the behavior: 16 | 17 | 1. Go to '...' 18 | 2. Run '...' 19 | 3. See error 20 | 21 | ## Expected behavior 22 | 23 | 24 | 25 | ## Extension (please complete the following information) 26 | 27 | - OS [e.g. Windows/Mac]: 28 | - OS Version [e.g. 1.140.1]: 29 | - Node Version [e.g. 10.15.3]: 30 | 31 | ## Are you willing to submit a PR? 32 | 33 | 34 | 35 | ## Did you search for similar existing issues? 36 | 37 | 38 | 39 | ## Additional context 40 | 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: feature request 6 | assignees: '' 7 | --- 8 | 9 | ## Is your feature request related to a problem? Please describe. 10 | 11 | 12 | ## Describe the desired outcome 13 | 14 | 15 | ## Describe alternatives you've considered 16 | 17 | 18 | ## Additional context 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/general_question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: General Question 3 | about: This is a template for people to ask questions that don't fit into any other issue categories 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | --- 8 | 9 | ## Your question here 10 | 11 | 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: nuget 4 | directory: "/csharp-selenium-webdriver-sample" 5 | schedule: 6 | interval: daily 7 | time: "08:30" # UTC 8 | open-pull-requests-limit: 10 # Default value of 5 has been problematic 9 | - package-ecosystem: npm 10 | directory: "/typescript-selenium-webdriver-sample" 11 | schedule: 12 | interval: daily 13 | time: "08:30" # UTC 14 | versioning-strategy: increase 15 | ignore: 16 | # Major version of @types/node is pinned to match the version of node we 17 | # use for builds (ideally, latest LTS) 18 | - dependency-name: "@types/node" 19 | versions: 20 | - ">=17.0.0" 21 | open-pull-requests-limit: 10 # Default value of 5 has been problematic 22 | - package-ecosystem: npm 23 | directory: "/typescript-playwright-sample" 24 | schedule: 25 | interval: daily 26 | time: "08:30" # UTC 27 | versioning-strategy: increase 28 | ignore: 29 | # Major version of @types/node is pinned to match the version of node we 30 | # use for builds (ideally, latest LTS) 31 | - dependency-name: "@types/node" 32 | versions: 33 | - ">=21.0.0" 34 | open-pull-requests-limit: 10 # Default value of 5 has been problematic 35 | -------------------------------------------------------------------------------- /.github/policies/resourceManagement.yml: -------------------------------------------------------------------------------- 1 | id: 2 | name: GitOps.PullRequestIssueManagement 3 | description: GitOps.PullRequestIssueManagement primitive 4 | owner: 5 | resource: repository 6 | disabled: false 7 | where: 8 | configuration: 9 | resourceManagementConfiguration: 10 | scheduledSearches: 11 | - description: 12 | frequencies: 13 | - hourly: 14 | hour: 3 15 | filters: 16 | - isOpen 17 | - isIssue 18 | - noActivitySince: 19 | days: 4 20 | - isNotLabeledWith: 21 | label: 'status: no recent activity' 22 | - hasLabel: 23 | label: 'status: needs author feedback' 24 | actions: 25 | - addLabel: 26 | label: 'status: no recent activity' 27 | - addReply: 28 | reply: This issue has been automatically marked as stale because it is marked as requiring author feedback but has not had any activity for **4 days**. It will be closed if no further activity occurs **within 3 days of this comment**. Thank you for contributing to Accessibility Insights! 29 | - description: 30 | frequencies: 31 | - hourly: 32 | hour: 3 33 | filters: 34 | - isOpen 35 | - isIssue 36 | - hasLabel: 37 | label: 'status: no recent activity' 38 | - noActivitySince: 39 | days: 3 40 | actions: 41 | - closeIssue 42 | eventResponderTasks: 43 | - if: 44 | - payloadType: Pull_Request 45 | - hasLabel: 46 | label: 'pr: auto-merge' 47 | then: 48 | - enableAutoMerge: 49 | mergeMethod: Squash 50 | description: 51 | - if: 52 | - payloadType: Pull_Request 53 | - labelRemoved: 54 | label: 'pr: auto-merge' 55 | then: 56 | - disableAutoMerge 57 | description: 58 | - if: 59 | - payloadType: Issues 60 | - or: 61 | - isAction: 62 | action: Opened 63 | - isAction: 64 | action: Reopened 65 | - not: 66 | hasLabel: 67 | label: task 68 | - not: isAssignedToSomeone 69 | then: 70 | - addLabel: 71 | label: 'status: new' 72 | - assignIcmUsers: 73 | teamId: 57436 74 | primary: True 75 | secondary: False 76 | description: 77 | - if: 78 | - payloadType: Issues 79 | - isAction: 80 | action: Opened 81 | - hasLabel: 82 | label: task 83 | then: 84 | - addLabel: 85 | label: 'status: new' 86 | description: 87 | - if: 88 | - payloadType: Issues 89 | - labelAdded: 90 | label: 'status: needs author feedback' 91 | then: 92 | - removeLabel: 93 | label: 'status: new' 94 | - addReply: 95 | reply: The team requires additional author feedback; please review their replies and update this issue accordingly. Thank you for contributing to Accessibility Insights! 96 | description: 97 | - if: 98 | - payloadType: Issues 99 | - isActivitySender: 100 | issueAuthor: True 101 | - hasLabel: 102 | label: 'status: needs author feedback' 103 | - not: 104 | isAction: 105 | action: Closed 106 | then: 107 | - addLabel: 108 | label: 'status: needs attention' 109 | - removeLabel: 110 | label: 'status: needs author feedback' 111 | description: 112 | - if: 113 | - payloadType: Issues 114 | - labelAdded: 115 | label: 'status: ready for triage' 116 | then: 117 | - assignTo: 118 | users: 119 | - nang4ally 120 | - addReply: 121 | reply: This issue has been marked as ready for team triage; we will triage it in our weekly review and update the issue. Thank you for contributing to Accessibility Insights! 122 | description: 123 | - if: 124 | - payloadType: Issues 125 | - labelAdded: 126 | label: 'status: needs investigation' 127 | then: 128 | - addReply: 129 | reply: 'This issue requires additional investigation by the Accessibility Insights team. When the issue is ready to be triaged again, we will update the issue with the investigation result and add "status: ready for triage". Thank you for contributing to Accessibility Insights!' 130 | - removeLabel: 131 | label: 'status: ready for triage' 132 | description: 133 | - if: 134 | - payloadType: Issues 135 | - labelAdded: 136 | label: good first issue 137 | then: 138 | - addLabel: 139 | label: help wanted 140 | description: 141 | - if: 142 | - payloadType: Issues 143 | - labelAdded: 144 | label: 'resolution: out of scope' 145 | then: 146 | - addReply: 147 | reply: 'This issue has been marked as being beyond the support scope of Accessibility Insights. It will now be closed automatically for house-keeping purposes. ' 148 | - closeIssue 149 | description: 150 | - if: 151 | - payloadType: Issues 152 | - or: 153 | - labelAdded: 154 | label: 'status: needs author feedback' 155 | - labelAdded: 156 | label: 'status: needs attention' 157 | - labelAdded: 158 | label: 'status: needs investigation' 159 | - labelAdded: 160 | label: 'status: blocked' 161 | - labelAdded: 162 | label: 'status: ready for triage' 163 | - labelAdded: 164 | label: 'status: ready for work' 165 | - labelAdded: 166 | label: 'status: active' 167 | - labelAdded: 168 | label: 'status: resolved' 169 | - labelAdded: 170 | label: 'resolution: out of scope' 171 | then: 172 | - removeLabel: 173 | label: 'status: new' 174 | description: 175 | - if: 176 | - payloadType: Issues 177 | - or: 178 | - labelAdded: 179 | label: 'status: new' 180 | - labelAdded: 181 | label: 'status: needs attention' 182 | - labelAdded: 183 | label: 'status: needs investigation' 184 | - labelAdded: 185 | label: 'status: blocked' 186 | - labelAdded: 187 | label: 'status: ready for triage' 188 | - labelAdded: 189 | label: 'status: ready for work' 190 | - labelAdded: 191 | label: 'status: active' 192 | - labelAdded: 193 | label: 'status: resolved' 194 | - labelAdded: 195 | label: 'resolution: out of scope' 196 | then: 197 | - removeLabel: 198 | label: 'status: needs author feedback' 199 | description: 200 | - if: 201 | - payloadType: Issues 202 | - or: 203 | - labelAdded: 204 | label: 'status: new' 205 | - labelAdded: 206 | label: 'status: needs author feedback' 207 | - labelAdded: 208 | label: 'status: needs investigation' 209 | - labelAdded: 210 | label: 'status: blocked' 211 | - labelAdded: 212 | label: 'status: ready for triage' 213 | - labelAdded: 214 | label: 'status: ready for work' 215 | - labelAdded: 216 | label: 'status: active' 217 | - labelAdded: 218 | label: 'status: resolved' 219 | - labelAdded: 220 | label: 'resolution: out of scope' 221 | then: 222 | - removeLabel: 223 | label: 'status: needs attention' 224 | description: 225 | - if: 226 | - payloadType: Issues 227 | - or: 228 | - labelAdded: 229 | label: 'status: new' 230 | - labelAdded: 231 | label: 'status: needs author feedback' 232 | - labelAdded: 233 | label: 'status: needs attention' 234 | - labelAdded: 235 | label: 'status: blocked' 236 | - labelAdded: 237 | label: 'status: ready for triage' 238 | - labelAdded: 239 | label: 'status: ready for work' 240 | - labelAdded: 241 | label: 'status: active' 242 | - labelAdded: 243 | label: 'status: resolved' 244 | - labelAdded: 245 | label: 'resolution: out of scope' 246 | then: 247 | - removeLabel: 248 | label: 'status: needs investigation' 249 | description: 250 | - if: 251 | - payloadType: Issues 252 | - or: 253 | - labelAdded: 254 | label: 'status: new' 255 | - labelAdded: 256 | label: 'status: needs author feedback' 257 | - labelAdded: 258 | label: 'status: needs attention' 259 | - labelAdded: 260 | label: 'status: needs investigation' 261 | - labelAdded: 262 | label: 'status: ready for triage' 263 | - labelAdded: 264 | label: 'status: ready for work' 265 | - labelAdded: 266 | label: 'status: active' 267 | - labelAdded: 268 | label: 'status: resolved' 269 | - labelAdded: 270 | label: 'resolution: out of scope' 271 | then: 272 | - removeLabel: 273 | label: 'status: blocked' 274 | description: 275 | - if: 276 | - payloadType: Issues 277 | - or: 278 | - labelAdded: 279 | label: 'status: new' 280 | - labelAdded: 281 | label: 'status: needs author feedback' 282 | - labelAdded: 283 | label: 'status: needs attention' 284 | - labelAdded: 285 | label: 'status: needs investigation' 286 | - labelAdded: 287 | label: 'status: blocked' 288 | - labelAdded: 289 | label: 'status: ready for work' 290 | - labelAdded: 291 | label: 'status: active' 292 | - labelAdded: 293 | label: 'status: resolved' 294 | - labelAdded: 295 | label: 'resolution: out of scope' 296 | then: 297 | - removeLabel: 298 | label: 'status: ready for triage' 299 | description: 300 | - if: 301 | - payloadType: Issues 302 | - or: 303 | - labelAdded: 304 | label: 'status: new' 305 | - labelAdded: 306 | label: 'status: needs author feedback' 307 | - labelAdded: 308 | label: 'status: needs attention' 309 | - labelAdded: 310 | label: 'status: needs investigation' 311 | - labelAdded: 312 | label: 'status: blocked' 313 | - labelAdded: 314 | label: 'status: ready for triage' 315 | - labelAdded: 316 | label: 'status: active' 317 | - labelAdded: 318 | label: 'status: resolved' 319 | - labelAdded: 320 | label: 'resolution: out of scope' 321 | then: 322 | - removeLabel: 323 | label: 'status: ready for work' 324 | description: 325 | - if: 326 | - payloadType: Issues 327 | - hasLabel: 328 | label: task 329 | - isAction: 330 | action: Closed 331 | then: 332 | - addLabel: 333 | label: 'status: resolved' 334 | - removeLabel: 335 | label: 'status: active' 336 | - removeLabel: 337 | label: 'status: new' 338 | description: 339 | - if: 340 | - payloadType: Issues 341 | - or: 342 | - labelAdded: 343 | label: 'status: new' 344 | - labelAdded: 345 | label: 'status: needs author feedback' 346 | - labelAdded: 347 | label: 'status: needs attention' 348 | - labelAdded: 349 | label: 'status: needs investigation' 350 | - labelAdded: 351 | label: 'status: blocked' 352 | - labelAdded: 353 | label: 'status: ready for triage' 354 | - labelAdded: 355 | label: 'status: ready for work' 356 | - labelAdded: 357 | label: 'status: resolved' 358 | - labelAdded: 359 | label: 'resolution: out of scope' 360 | then: 361 | - removeLabel: 362 | label: 'status: active' 363 | description: 364 | - if: 365 | - payloadType: Issues 366 | - or: 367 | - labelAdded: 368 | label: 'status: new' 369 | - labelAdded: 370 | label: 'status: needs author feedback' 371 | - labelAdded: 372 | label: 'status: needs attention' 373 | - labelAdded: 374 | label: 'status: needs investigation' 375 | - labelAdded: 376 | label: 'status: blocked' 377 | - labelAdded: 378 | label: 'status: ready for triage' 379 | - labelAdded: 380 | label: 'status: ready for work' 381 | - labelAdded: 382 | label: 'status: active' 383 | - labelAdded: 384 | label: 'resolution: out of scope' 385 | then: 386 | - removeLabel: 387 | label: 'status: resolved' 388 | description: 389 | - if: 390 | - payloadType: Issue_Comment 391 | - isActivitySender: 392 | issueAuthor: True 393 | - hasLabel: 394 | label: 'status: needs author feedback' 395 | then: 396 | - addLabel: 397 | label: 'status: needs attention' 398 | - removeLabel: 399 | label: 'status: needs author feedback' 400 | description: 401 | onFailure: 402 | onSuccess: 403 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | #### Details 2 | 3 | 4 | 5 | ##### Motivation 6 | 7 | 8 | 9 | ##### Context 10 | 11 | 12 | 13 | 14 | 15 | #### Pull request checklist 16 | 17 | 18 | - [ ] If this PR addresses an existing issue, it is linked: Fixes #0000 19 | - [ ] New sample content is commented at a similar verbosity as existing content 20 | - [ ] All updated/modified sample code builds and runs in at least one PR/CI build 21 | - [ ] PR checks for builds named `[failing example] ...` pass in the PR build, then confirm they fail in the CI build 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This project welcomes contributions and suggestions. 4 | 5 | For larger contributions (eg, a new sample), we recommend starting out by filing an issue (use the "Suggestion" template) to discuss your idea with us first. 6 | 7 | For small contributions (eg, a minor fix to an existing sample), feel free to just send a Pull Request. 8 | 9 | ## Pull Requests 10 | 11 | Before sending a pull request, please double check that: 12 | 13 | * Any new sample content being added is documented with a sample-level README.md and verbosely commented inline. 14 | * The tests run and pass in each sample you're modifying. 15 | 16 | Pull requests will be reviewed and merged by a member of the Accessibility Insights team. 17 | 18 | ## Contributor License Agreement 19 | 20 | Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com. 21 | 22 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. 23 | 24 | ## Code of Conduct 25 | 26 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Axe + Azure Pipelines: Automate accessibility testing in your CI builds 2 | 3 | Watch a [short intro video](https://www.youtube.com/watch?v=SarmnCULt8M) to get started with automated accessibility tests in Azure pipelines. Please note that these samples are applicable to web projects that have UI automation tests. Benefits include, 4 | 5 | - Automated accessibility tests run during Continuous Integration and Pull Request builds, ensuring that all code changes are free of common easily-detected accessibility issues before they go to production 6 | - Builds can be configured to fail based on the results of the automated accessibility tests, preventing both new accessibility bugs and regressions 7 | - Failure details and _how to fix_ information can be viewed in Azure DevOps under Builds, making it quick and easy to investigate and resolve the accessibility issues 8 | 9 | This repository contains sample projects (see next section [Available samples](#available-samples)) demonstrating how to implement automated accessibility testing in [Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/) builds using [axe-core](https://github.com/dequelabs/axe-core), the same accessibility scanning engine used in [Accessibility Insights for Web](https://accessibilityinsights.io/docs/en/web/overview), and [Axe.Windows](https://github.com/microsoft/axe-windows), the same accessibility scanning engine used in [Accessibility Insights for Windows](https://accessibilityinsights.io/docs/en/windows/overview/). 10 | 11 | ## Available samples 12 | 13 | The following sample projects specify the main technologies used. A team that uses comparable tools and frameworks should be able to refer to the sample and update their existing tests to incorporate automated accessibility checks. 14 | 15 | - **[Sample 1: typescript-playwright-sample](./typescript-playwright-sample)**: 16 | 17 | - Useful for teams using Playwright and TypeScript/JavaScript. 18 | 19 | - **[Sample 2: typescript-selenium-webdriver-sample](./typescript-selenium-webdriver-sample)**: 20 | 21 | - Useful for teams using Selenium and TypeScript/JavaScript. 22 | 23 | - **[Sample 3: CSharpSeleniumWebdriverSample](./csharp-selenium-webdriver-sample)**: 24 | 25 | - Useful for teams using Selenium and C#. 26 | 27 | ## Other Resources 28 | 29 | The following may be helpful as an example of how our accessibility scanning tools have been used where the product is not based on HTML: 30 | 31 | - **[WindowsAppResources](./windows-app-resources)**: 32 | 33 | - Useful for teams developing non-browser-based Windows applications. These applications can use any language or framework. 34 | 35 | ## Requests 36 | 37 | _Are we missing a sample or resource you'd like to see? [File a sample request](https://github.com/microsoft/axe-pipelines-samples/issues/new?assignees=&labels=sample_request&template=feature_request.md&title=Sample+Request%3A+%3Csample+name+here%3E) or [submit a pull request](./CONTRIBUTING.md)!_ 38 | 39 | ## Disclaimer 40 | 41 | Automated accessibility tests can detect some common accessibility problems such as missing or invalid properties. But most accessibility problems can only be discovered through manual testing. We recommend **both** automated testing, to continuously protect against simple types of issues, and regular manual assessments with [Accessibility Insights for Web](https://accessibilityinsights.io/docs/en/web/overview), a free and open source dev tool that walks you through assessing a website for [WCAG 2.1 AA](https://www.w3.org/WAI/WCAG21/quickref/?currentsidebar=%23col_customize&levels=aaa) coverage. 42 | 43 | ## Contributing 44 | 45 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 46 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 47 | the rights to use your contribution. For details, visit https://cla.microsoft.com. 48 | 49 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 50 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions 51 | provided by the bot. You will only need to do this once across all repos using our CLA. 52 | 53 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 54 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 55 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 56 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /csharp-selenium-webdriver-sample/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | 254 | # Solution File for the purpose of this project only 255 | *.sln -------------------------------------------------------------------------------- /csharp-selenium-webdriver-sample/CSharpSeleniumWebdriverSample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | {E6205F43-94B0-462C-AA5B-AE816794E95A} 4 | net6.0 5 | CSharpSeleniumWebdriverSample 6 | CSharpSeleniumWebdriverSample 7 | 8 | 9 | 10 | Always 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /csharp-selenium-webdriver-sample/README.md: -------------------------------------------------------------------------------- 1 | # CSharpSeleniumWebdriverSample 2 | 3 | This sample demonstrates how you might set up a C# Project with an Azure Pipelines build that runs end to end accessibility tests in a browser. 4 | 5 | ## Getting Started 6 | 7 | The individual files in the sample contain comments that explain the important parts of each file in context. 8 | 9 | Some good places to start reading are: 10 | 11 | * [SamplePageTests.cs](./SamplePageTests.cs): C# test file that opens [SamplePage.html](./SamplePage.html) in a browser with Selenium and runs accessibility scans against it 12 | * [azure-pipelines.yml](./azure-pipelines.yml): Azure Pipelines config file that sets up our Continuous Integration and Pull Request builds 13 | 14 | ## Tools and libraries used 15 | 16 | The key tools and libraries this sample demonstrates are: 17 | 18 | * [Selenium.WebDriver](https://www.seleniumhq.org), the .NET library for Selenium, a tool for automating interactions with different web browsers. 19 | * [Selenium.Axe](https://github.com/TroyWalshProf/SeleniumAxeDotnet), a .NET library for running accessibility scans on web pages by using Selenium.WebDriver to run the [axe-core](https://github.com/dequelabs/axe-core) accessibility scanning engine. 20 | * [Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/) to run the tests in a CI build with every Pull Request. 21 | 22 | This sample also uses a few other tools and libraries which are less important; if you use a different test framework or a different version of .NET, you can still follow along with most of the sample. 23 | 24 | * [.NET Core](https://dotnet.microsoft.com) to build/run the code. 25 | * None of the libraries or C# code in the sample are specific to .NET Core; if your project uses the full .NET Framework, you can still follow along with the sample! 26 | * [MSTest](https://github.com/microsoft/testfx) as our test framework. 27 | * None of the other libraries we're using require this particular framework; you can still follow along with the sample using NUnit, xUnit, or any other test framework you like. 28 | * [Chrome](https://google.com/chrome) (with [Selenium.WebDriver.ChromeDriver](https://github.com/jsakamoto/nupkg-selenium-webdriver-chromedriver/)) and [Firefox](https://www.mozilla.org/firefox/) (with [Selenium.WebDriver.GeckoDriver](https://github.com/jsakamoto/nupkg-selenium-webdriver-geckodriver/)) as our test browsers 29 | * Selenium supports many different browsers and operating systems; use whichever combination is most important for your product! 30 | * [FluentAssertions](https://fluentassertions.com/) to write test assertions 31 | * We like FluentAssertions because it gives great error messages out-of-the-box with Selenium.Axe. But you can still follow the rest of the sample if you prefer a different assertion style! 32 | 33 | ## See it in action on your local machine 34 | 35 | 1. Install the [.NET Core SDK](https://dotnet.microsoft.com/download) 36 | 1. Install the stable version of [Chrome](https://www.google.com/chrome/) 37 | 1. Install the stable version of [Firefox](https://www.mozilla.org/en-US/firefox/) 38 | 1. Clone this sample repository 39 | 40 | ```sh 41 | git clone https://github.com/microsoft/axe-pipelines-samples 42 | ``` 43 | 44 | 1. Run the tests 45 | 46 | Filter to only run passing tests: 47 | 48 | ```sh 49 | cd ./axe-pipelines-samples/csharp-selenium-webdriver-sample 50 | dotnet test --filter TestCategory!=IntentionallyFailsAsAnExample 51 | ``` 52 | 53 | ![Screenshot of 'dotnet test' command showing all tests passing](./Resources/screenshot-dotnet-test-success.png) 54 | 55 | Run all tests (both passing and failing): 56 | 57 | ```sh 58 | cd ./axe-pipelines-samples/csharp-selenium-webdriver-sample 59 | dotnet test 60 | ``` 61 | 62 | ![Screenshot of 'dotnet test' command showing some accessibility violations](./Resources/screenshot-dotnet-test-failure.png) 63 | 64 | ## See it in action in Azure Pipelines 65 | 66 | * Example build with failures: [![Azure Pipelines Build Status for failing example build](https://dev.azure.com/accessibility-insights/axe-pipelines-samples/_apis/build/status/%5Bfailing%20example%5D%20csharp-selenium-webdriver-sample?branchName=main)](https://dev.azure.com/accessibility-insights/axe-pipelines-samples/_build/latest?definitionId=33&branchName=main) 67 | * Example build without failures: [![Azure Pipelines Build Status for passing example build](https://dev.azure.com/accessibility-insights/axe-pipelines-samples/_apis/build/status/%5Bpassing%20example%5D%20csharp-selenium-webdriver-sample?branchName=main)](https://dev.azure.com/accessibility-insights/axe-pipelines-samples/_build/latest?definitionId=32&branchName=main) 68 | 69 | 73 | The accessibility tests run as part of the `dotnet test` build step: 74 | 75 | [![Screenshot of "dotnet test" build logs in sample build](./Resources/screenshot-logs-tab.png)](https://dev.azure.com/accessibility-insights/axe-pipelines-samples/_build/results?buildId=2338&view=logs&j=12f1170f-54f2-53f3-20dd-22fc7dff55f9) 76 | 77 | The test pass/fail results display in the Tests tab of the build logs: 78 | 79 | [![Screenshot of Tests tab in sample build](./Resources/screenshot-tests-tab.png)](https://dev.azure.com/accessibility-insights/axe-pipelines-samples/_build/results?buildId=2338&view=ms.vss-test-web.build-test-results-tab&runId=6512&resultId=100000&paneView=debug) 80 | -------------------------------------------------------------------------------- /csharp-selenium-webdriver-sample/Resources/screenshot-dotnet-test-failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/axe-pipelines-samples/32d272945fc3056c0cc35740e85b0a8306346cb4/csharp-selenium-webdriver-sample/Resources/screenshot-dotnet-test-failure.png -------------------------------------------------------------------------------- /csharp-selenium-webdriver-sample/Resources/screenshot-dotnet-test-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/axe-pipelines-samples/32d272945fc3056c0cc35740e85b0a8306346cb4/csharp-selenium-webdriver-sample/Resources/screenshot-dotnet-test-success.png -------------------------------------------------------------------------------- /csharp-selenium-webdriver-sample/Resources/screenshot-logs-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/axe-pipelines-samples/32d272945fc3056c0cc35740e85b0a8306346cb4/csharp-selenium-webdriver-sample/Resources/screenshot-logs-tab.png -------------------------------------------------------------------------------- /csharp-selenium-webdriver-sample/Resources/screenshot-tests-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/axe-pipelines-samples/32d272945fc3056c0cc35740e85b0a8306346cb4/csharp-selenium-webdriver-sample/Resources/screenshot-tests-tab.png -------------------------------------------------------------------------------- /csharp-selenium-webdriver-sample/SamplePage.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Example page with some accessibility issues 5 | 6 | 7 |
8 |

This is a static sample page with some accessibility issues

9 |

Here are some examples of content without axe-core violations

10 |

There's nothing wrong with this paragraph!

11 |

There's also nothing wrong with this paragraph!

12 | 13 |

Here are some examples of content with different types of axe-core violations

14 | 19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /csharp-selenium-webdriver-sample/SamplePageTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | // This sample happens to use .NET Core, but you can use whichever .NET version makes sense for your project. 5 | // Everything we're demonstrating would also work in .NET Framework 4.5+ with no modifications. 6 | using System; 7 | using System.IO; 8 | // This sample happens to use MSTest, but you can use whichever test framework you like. 9 | // Everything we're demonstrating would also work with xUnit, NUnit, or any other test framework. 10 | using Microsoft.VisualStudio.TestTools.UnitTesting; 11 | // If you're using Selenium already, you're probably already using these. 12 | using OpenQA.Selenium; 13 | using OpenQA.Selenium.Support.UI; 14 | // These are the important new libraries we're demonstrating. 15 | // You'll probably need to add new NuGet package references for these. 16 | using Selenium.Axe; 17 | using FluentAssertions; 18 | 19 | namespace CSharpSeleniumWebdriverSample 20 | { 21 | [TestClass] 22 | public class SamplePageTests 23 | { 24 | #region Example test methods 25 | 26 | // This test case shows the most basic example: run an accessibility scan on a page and assert that there are no violations. 27 | [TestMethod] 28 | [TestCategory("IntentionallyFailsAsAnExample")] 29 | public void TestAccessibilityOfPage() 30 | { 31 | AxeResult axeResult = new AxeBuilder(_webDriver) 32 | // This WithTags directive restricts Axe to only run tests that detect known violations of WCAG 2.1 A and AA rules 33 | // (similar to what Accessibility Insights reports). If you omit this, Axe will additionally run several "best practice" 34 | // rules, which are good ideas to check for periodically but may report false positives in certain edge cases. 35 | // 36 | // For complete documentation of which rule IDs and tags axe supports, see: 37 | // * summary of rules with IDs and tags: https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md 38 | // * full reference documentation for each rule: https://dequeuniversity.com/rules/axe 39 | .WithTags("wcag2a", "wcag2aa", "wcag21a", "wcag21aa") 40 | .Analyze(); 41 | 42 | // axeResult.Violations is an array of all the accessibility violations the scan found; the easiest way to assert 43 | // that a scan came back clean is to assert that the Violations array is empty. You can do this with the built in 44 | // MSTest assertions like this: 45 | // 46 | // Assert.AreEqual(0, axeResult.Violations.Length); 47 | // 48 | // However, we don't recommend using Assert.AreEqual for this because it doesn't give very useful error messages if 49 | // it does detect a violation; the error message will just say "expected 0 but found 1". 50 | // 51 | // We recommend using FluentAssertions instead; its default behavior gives much better error messages that include 52 | // full descriptions of accessibility issues, including links to detailed guidance at https://dequeuniversity.com 53 | // and CSS selector paths that exactly identify the element on the page with the issue. 54 | axeResult.Error.Should().BeNull(); 55 | 56 | // Our PR builds do not change the presence or absence of accessibility issues, so we special case 57 | // our PR build tests to expect the errors. This is not recommended for most projects, but since the 58 | // check takes effect only if the ACCESSIBILITY_ERRORS_ARE_EXPECTED_IN_PR_BUILD variable is set to 59 | // true within the pipeline, you may safely copy this code and the special case will be ignored. 60 | if (AccessibilityErrorsAreExpectedInThisBuild()) 61 | { 62 | axeResult.Violations.Should().NotBeEmpty(); 63 | } 64 | else 65 | { 66 | axeResult.Violations.Should().BeEmpty(); 67 | } 68 | } 69 | 70 | // This test case shows 2 options for scanning specific elements within a page, rather than an entire page. 71 | [TestMethod] 72 | public void TestAccessibilityOfIndividualElements() 73 | { 74 | // Both of these 2 options work equally well; which one to use is a matter of preference. 75 | 76 | // Option 1: using Selenium's FindElement to identify the element to test 77 | // 78 | // This can be simpler if your test already had to find the element for earlier assertions, or if you want to test 79 | // an element that is hard to identify using a CSS selector. 80 | IWebElement elementUnderTest = _webDriver.FindElement(By.Id("id-of-example-accessible-element")); 81 | 82 | AxeResult axeResultWithAnalyzeWebElement = new AxeBuilder(_webDriver) 83 | .WithTags("wcag2a", "wcag2aa", "wcag21a", "wcag21aa") 84 | .Analyze(elementUnderTest); 85 | 86 | axeResultWithAnalyzeWebElement.Error.Should().BeNull(); 87 | axeResultWithAnalyzeWebElement.Violations.Should().BeEmpty(); 88 | 89 | // Option 2: using AxeBuilder.Include 90 | // 91 | // This can be simpler if you need to test multiple elements at once or need to deal with