-
Notifications
You must be signed in to change notification settings - Fork 64
/
Copy pathSamplePageTests.cs
205 lines (176 loc) · 11.6 KB
/
SamplePageTests.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// This sample happens to use .NET Core, but you can use whichever .NET version makes sense for your project.
// Everything we're demonstrating would also work in .NET Framework 4.5+ with no modifications.
using System;
using System.IO;
// This sample happens to use MSTest, but you can use whichever test framework you like.
// Everything we're demonstrating would also work with xUnit, NUnit, or any other test framework.
using Microsoft.VisualStudio.TestTools.UnitTesting;
// If you're using Selenium already, you're probably already using these.
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
// These are the important new libraries we're demonstrating.
// You'll probably need to add new NuGet package references for these.
using Selenium.Axe;
using FluentAssertions;
namespace CSharpSeleniumWebdriverSample
{
[TestClass]
public class SamplePageTests
{
#region Example test methods
// This test case shows the most basic example: run an accessibility scan on a page and assert that there are no violations.
[TestMethod]
[TestCategory("IntentionallyFailsAsAnExample")]
public void TestAccessibilityOfPage()
{
AxeResult axeResult = new AxeBuilder(_webDriver)
// This WithTags directive restricts Axe to only run tests that detect known violations of WCAG 2.1 A and AA rules
// (similar to what Accessibility Insights reports). If you omit this, Axe will additionally run several "best practice"
// rules, which are good ideas to check for periodically but may report false positives in certain edge cases.
//
// For complete documentation of which rule IDs and tags axe supports, see:
// * summary of rules with IDs and tags: https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md
// * full reference documentation for each rule: https://dequeuniversity.com/rules/axe
.WithTags("wcag2a", "wcag2aa", "wcag21a", "wcag21aa")
.Analyze();
// axeResult.Violations is an array of all the accessibility violations the scan found; the easiest way to assert
// that a scan came back clean is to assert that the Violations array is empty. You can do this with the built in
// MSTest assertions like this:
//
// Assert.AreEqual(0, axeResult.Violations.Length);
//
// However, we don't recommend using Assert.AreEqual for this because it doesn't give very useful error messages if
// it does detect a violation; the error message will just say "expected 0 but found 1".
//
// We recommend using FluentAssertions instead; its default behavior gives much better error messages that include
// full descriptions of accessibility issues, including links to detailed guidance at https://dequeuniversity.com
// and CSS selector paths that exactly identify the element on the page with the issue.
axeResult.Error.Should().BeNull();
// Our PR builds do not change the presence or absence of accessibility issues, so we special case
// our PR build tests to expect the errors. This is not recommended for most projects, but since the
// check takes effect only if the ACCESSIBILITY_ERRORS_ARE_EXPECTED_IN_PR_BUILD variable is set to
// true within the pipeline, you may safely copy this code and the special case will be ignored.
if (AccessibilityErrorsAreExpectedInThisBuild())
{
axeResult.Violations.Should().NotBeEmpty();
}
else
{
axeResult.Violations.Should().BeEmpty();
}
}
// This test case shows 2 options for scanning specific elements within a page, rather than an entire page.
[TestMethod]
public void TestAccessibilityOfIndividualElements()
{
// Both of these 2 options work equally well; which one to use is a matter of preference.
// Option 1: using Selenium's FindElement to identify the element to test
//
// This can be simpler if your test already had to find the element for earlier assertions, or if you want to test
// an element that is hard to identify using a CSS selector.
IWebElement elementUnderTest = _webDriver.FindElement(By.Id("id-of-example-accessible-element"));
AxeResult axeResultWithAnalyzeWebElement = new AxeBuilder(_webDriver)
.WithTags("wcag2a", "wcag2aa", "wcag21a", "wcag21aa")
.Analyze(elementUnderTest);
axeResultWithAnalyzeWebElement.Error.Should().BeNull();
axeResultWithAnalyzeWebElement.Violations.Should().BeEmpty();
// Option 2: using AxeBuilder.Include
//
// This can be simpler if you need to test multiple elements at once or need to deal with <iframe>s.
AxeResult axeResultWithInclude = new AxeBuilder(_webDriver)
.Include("#id-of-example-accessible-element")
.Include(".class-of-example-accessible-element")
.Include("#id-of-iframe", "#id-of-element-inside-iframe")
.WithTags("wcag2a", "wcag2aa", "wcag21a", "wcag21aa")
.Analyze();
axeResultWithInclude.Error.Should().BeNull();
axeResultWithInclude.Violations.Should().BeEmpty();
}
// This test case shows how you might baseline some known/pre-existing accessibility violations from your tests.
// This is useful when just getting started with accessibility testing, so you can prevent new issues from creeping in
// while you're still working on fixing any existing issues.
[TestMethod]
public void TestAccessibilityOfPageExcludingKnownIssues()
{
// You can use AxeBuilder.Exclude to exclude individual elements with known issues from a scan.
//
// Exclude accepts any CSS selector; you could also use ".some-classname" or "div[some-attr='some value']"
AxeResult axeResultExcludingExampleViolationsElement = new AxeBuilder(_webDriver)
.Exclude("#id-of-example-accessibility-violation-list")
.Analyze();
axeResultExcludingExampleViolationsElement.Error.Should().BeNull();
axeResultExcludingExampleViolationsElement.Violations.Should().BeEmpty();
// You can also use AxeBuilder.DisableRules to exclude certain individual rules from a scan. This is particularly
// useful if you still want to perform *some* scanning on the elements you exclude from more broad scans.
AxeResult axeResultDisablingRulesViolatedByExamples = new AxeBuilder(_webDriver)
.Include("#id-of-example-accessibility-violation-list") // Like Exclude(), accepts any CSS selector
.DisableRules("color-contrast", "label", "tabindex")
.Analyze();
axeResultDisablingRulesViolatedByExamples.Error.Should().BeNull();
axeResultDisablingRulesViolatedByExamples.Violations.Should().BeEmpty();
// Another option is to assert on the size of the Violations array. This works just fine, but we recommend the
// other options above as your first choice instead because when they do find new issues, they will produce error
// messages that more clearly identify exactly what the new/unexpected issues are.
AxeResult axeResult = new AxeBuilder(_webDriver).Analyze();
axeResult.Error.Should().BeNull();
axeResult.Violations.Should().HaveCount(3);
}
#endregion
#region Example setup and cleanup methods
// The rest of this file shows examples of how to set up an IWebDriver instance to connect to a browser and
// navigate to a test page.
//
// If you're incorporating accessibility testing into an existing body of end to end tests, you can stick with
// however your existing tests are already solving this; you don't need to do anything special to use Selenium.Axe.
// Starting a new browser process is good for keeping tests isolated from one another, but can be slow. Here, we're
// using a [ClassInitialize] method so the same browser will be shared between different [TestMethod]s.
[ClassInitialize]
public static void StartBrowser(TestContext testContext) {
// WebDriverFactory uses environment variables set by azure-pipelines.yml to determine which browser to use;
// the test cases we'll write in this file will work regardless of which browser they're running against.
//
// This WebDriverFactory is just one example of how you might initialize Selenium; if you're adding Selenium.Axe
// to an existing set of end to end tests that already have their own way of initializing a webdriver, you can
// keep using that instead.
_webDriver = WebDriverFactory.CreateFromEnvironmentVariableSettings();
// You *must* set this timeout to use Selenium.Axe. It defaults to "0 seconds", which isn't enough time for
// Axe to scan the page. The exact amount of time will depend on the complexity of the page you're testing.
_webDriver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromSeconds(20);
}
// It's important to remember to clean up the browser and webdriver; otherwise, the browser process will remain
// alive after your tests are done, which can cause confusing errors during later builds and/or test sessions.
[ClassCleanup]
public static void StopBrowser()
{
_webDriver?.Quit();
}
// Reloading the test page before every test isn't strictly necessary (since these accessibility scans aren't
// modifying the page), but is a good practice to default to in the interest of making sure different tests
// can execute independently.
[TestInitialize]
public void LoadTestPage() {
// For simplicity, we're pointing our test browser directly to a static html file on disk.
// In a project with more complex hosting needs, you might instead start up a localhost http server
// and then navigate to a http://localhost link.
string samplePageFilePath = Path.GetFullPath(@"SamplePage.html");
string samplePageFileUrl = new Uri(samplePageFilePath).AbsoluteUri;
_webDriver.Navigate().GoToUrl(samplePageFileUrl);
// It's a good practice to make sure the page's content has actually loaded *before* running any
// accessibility tests. This acts as a sanity check that the browser initialization worked and makes
// sure that you aren't just scanning a blank page.
new WebDriverWait(_webDriver, TimeSpan.FromSeconds(10))
.Until(d => d.FindElement(By.TagName("main")));
}
private static IWebDriver _webDriver;
#endregion
#region Helper methods
private bool AccessibilityErrorsAreExpectedInThisBuild() {
return
Environment.GetEnvironmentVariable("ACCESSIBILITY_ERRORS_ARE_EXPECTED_IN_PR_BUILD") == "true" &&
Environment.GetEnvironmentVariable("BUILD_SOURCEBRANCH") != "refs/heads/main";
}
#endregion
}
}