Статьи

Селен и Раздел 508

Selenium — популярный инструмент для тестирования веб-приложений. Оно было разработано с 2004 года и первоначально предназначалось для приемочного тестирования приложения для отчетности о времени и расходах для ThoughtWorks . Члены ThoughtWorks все еще участвуют в его разработке, как и другие, на веб-сайте OpenQA .

Selenium написан на JavaScript и работает в браузере. Существует несколько версий инструмента: самая простая для развертывания и тестирования — Selenium IDE. Это доступно как расширение Firefox здесь . После установки расширение очень просто в использовании. Он позволяет пользователям записывать тесты, просто нажимая и печатая их, а также редактировать и воспроизводить их по мере необходимости. Вы также можете создавать тесты вручную, если хотите.

Возможно, самым простым в использовании является интерфейс редактора:

[img_assist | nid = 6954 | title = IDE Selenium | desc = | link = none | align = center | width = 400 | height = 520]

Для каждой команды в каждом тесте можно заполнить три строки, которые отображаются в интерфейсе редактора как Command, Target и Value. Не все команды требуют всех трех; Конечно, все команды требуют заполнения командной строки. Каждая команда имеет свой собственный набор параметров. Они хранятся в HTML-файлах IDE со структурой команд, которая выглядит следующим образом:

  <tr>
    <td>Command</td>
    <td>Target</td>
    <td>Value</td>
  </tr>

Я на самом деле предпочитаю HTML-форму тестов и часто строю тесты непосредственно в HTML (это можно сделать на вкладке « Источник » интерфейса или в вашем любимом текстовом редакторе). Ваш пробег, конечно, может отличаться, но я буду использовать HTML-форму тестов в этой статье.

Selenium регистрирует состояние своего тестирования (JavaScript, конечно, не может записывать в файлы) в консоли журнала . Это просто таблица результатов, созданная во время выполнения. Некоторые тесты Selenium дают далеко не идеальную обратную связь о том, что именно пошло не так, но даже ограниченные сообщения об ошибках часто могут быть полезны.

Недостатком использования Selenium IDE является то, что он работает только в Firefox. Я редко нахожу это ограничение, и оно не относится к данной статье. Большинство тестов здесь используют XPath для идентификации вещей, которые должны или не должны существовать в веб-приложении; Если тесты Firefox пройдут успешно, с веб-приложением все в порядке, в каком бы браузере оно ни просматривалось. Если решения Selenium требуются в других браузерах, нетрудно использовать IDE для преобразования тестов в другие языки, например Java (где они преобразуются в тесты JUnit); на этих языках обычно поддерживаются другие браузеры, но IDE использовать нельзя.
Одним из чрезвычайно полезных аспектов Selenium является то, что он позволяет расширять функции, которые он предоставляет, добавляя свои собственные. Я делаю это довольно часто; Есть целый ряд внесенных пользователем расширений здесь . Мы увидим два примера этого позже.
Selenium стал абсолютно необходимой частью моего режима разработки и тестирования. Это делает возможным разработку через тестирование в веб-среде без особых хлопот и беспокойств.

Раздел 508

Раздел 508является частью Закона о реабилитации 1973 года с поправками 1998 года (29 USC § 794d). Люди с ограниченными возможностями часто находили веб-сайт запрещающим и недоступным, поскольку он часто даже не учитывал минимально потребности людей, чей подход к Интернету не основан на визуальном подходе или использовании мыши. Идея Раздела 508 состояла в том, чтобы сделать такую ​​информацию (не только веб-приложения, но это является нашей целью здесь) доступной, устраняя барьеры, которые сдерживали пользователей, и поощрять разработку технологий, которые упростили бы доступность с течением времени. Закон распространяется на федеральные агентства, но, как стандарт, это хорошая вещь, которую следует иметь в виду при разработке для Интернета: ничто в Разделе 508 не требует усложнять использование чего-либо для любого пользователя, а предоставление информации для большего числа пользователей просто увеличивает нашего пользователя. основание.

Цель этой статьи

Во многих случаях адаптации, требуемые для соответствия стандартам Раздела 508, не являются обременительными. Скорее, это вещи, которые мы склонны забывать, когда сами не используем их регулярно.

Цель настоящей статьи — показать, что тестирование Selenium может эффективно использоваться в качестве памятной записки , гарантируя, что мы не забудем добавить функции, которые сделают наши веб-сайты более доступными, и что мы не добавим функции это сделает их менее доступными.

Я лично создаю тестовый сценарий для своих приложений, использующих JSP, который просто переходит (и, следовательно, компилирует) все JSP автоматически, в процессе тестирования на соответствие требованиям Раздела 508. Автоматизированный как часть моего процесса сборки, я считаю, что это полезный способ избежать «зависания» времени, которое JSP могут иногда (и раздражающе) вызывать, и в то же время гарантировать, что я не упустил шанс на соответствие Разделу 508 это стоит мне немного, если что-нибудь и дает доступ.

Теги скрипта

Хотя внешние таблицы стилей (с использованием тега ссылки ) могут быть переопределены пользователями с конкретными проблемами, что позволяет им изменять внешний вид HTML-страницы в соответствии со своими конкретными потребностями, встроенные стили CSS, определенные в HTML, не могут быть переопределены. Сам тест для выявления случаев этого является простой проверкой количества элементов стиля на странице HTML с использованием XPath ( опция проверки в Selenium не останавливает тестовый случай, даже если он должен сообщить о неудаче теста ):

  <tr>
<td>verifyXpathCount</td>
<td>//style</td>
<td>0</td>
</tr>

 

Alt атрибуты (раунд 1)

Each content-carrying image should have an alt textual description for non-visual users. Were it this simple, the test would be relatively straightforward:

  <tr>
<td>verifyXpathCount</td>
<td>//img[not(normalize-space(@alt))]</td>
<td>0</td>
</tr>

As we shall see later, this rule comes with an exception, which may or may not apply to your site.

The same logic applies to various animation types (Java applets, Flash files, video files, audio files, and other plug-ins):

  <tr>
<td>verifyXpathCount</td>
<td>//applet[not(normalize-space(@alt))]</td>
<td>0</td>
</tr>
<tr>
<td>verifyXpathCount</td>
<td>//object[not(normalize-space())]</td>
<td>0</td>
</tr>

Again, your specific cases may differ slightly, but this should point the way towards a solution.

Tables

Table columns should be headed by «th» tags to make the logical organization of data clear. Here the test is trivial:

  <tr>
<td>verifyXpathCount</td>
<td>//table[not(descendant::th)]</td>
<td>0</td>
</tr>

Конечно, это предполагает правильный HTML. Если вы все еще используете таблицы для макета страницы (позор разработчиков браузера, которые заставляют вас делать это с их ошибочными реализациями CSS!), Ситуация более сложная. В этом случае я бы предложил использовать атрибут класса для различения таблиц без макета (скажем, «class = ‘layout’»). Тогда решение не намного хуже:

  <tr>
<td>verifyXpathCount</td>
<td>//table[not(@class='layout') and not(descendant::th)]</td>
<td>0</td>
</tr>
<tr>
<td>verifyXpathCount</td>
<td>//table[@class='layout' and (descendant::th or descendant::td/@headers)]</td>
<td>0</td>
</tr>

Конечно, вторая проверка гарантирует, что в чисто таблицах компоновки нет элементов th (или элементов td, использующих
атрибут
headers ), что может привести к путанице, особенно для программ чтения с экрана аудио.
Объем атрибут (равный «Col») или
заголовки атрибут должен быть использован для столбцов:


  <tr>
<td>verifyXpathCount</td>
<td>//table[descendant::th[normalize-space(@scope)!='col' and not(normalize-space(@headers))]]</td>
<td>0</td>
</tr>

Если вы используете таблицы макетов, группы столбцов или то, что спецификация HTML очаровательно называет «осями в n-мерном пространстве» (т.
Е. Атрибутом оси ), то, конечно, XPath необходимо будет скорректировать.
Объем атрибут (равный «строка») или
заголовки атрибут также должен быть использован для первой ячейки в строках:


  <tr>
<td>verifyXpathCount</td>
<td>//table[descendant::td[position()=1 and normalize-space(@scope)!='row' and not(normalize-space(@headers))]]</td>
<td>0</td>
</tr>

 Этикетки

The use of labels for various form data input fields is another important aspect of the logical organization of information. The label for attribute must match up exactly with the id attribute of the field to which it refers:

  <tr>
<td>verifyXpathCount</td>
<td>//input[not(//label/@for=./@id) and normalize-space(@type)!='hidden' and normalize-space(@type)!='button' and normalize-space(@type)!='submit' and normalize-space(@type)!='reset' and normalize-space(@type)!='image']</td>
<td>0</td>
</tr>
<tr>
<td>verifyXpathCount</td>
<td>//select[not(//label/@for=./@id)]</td>
<td>0</td>
</tr>
<tr>
<td>verifyXpathCount</td>
<td>//textarea[not(//label/@for=./@id)]</td>
<td>0</td>
</tr>

This can get a bit more complex with tables including checkboxes or radio buttons, which may legitimately not have labels.

Adding a class (possibly «radioTable») to the table and then adding

and not(normalize-space(@type)='radio' and ancestor::table[@class='radioTable'])

to the first of the above XPath expressions (just before the last square bracket) should help. You may, of course, use another predicate to identify the table, such as an id or other attribute.

Frames

In a perfect world, we wouldn’t need frames at all, but in this fallen one, they are sometimes useful. They should always have titles describing their contents:

  <tr>
<td>verifyXpathCount</td>
<td>//frame[not(normalize-space(@title))]</td>
<td>0</td>
</tr>

Image maps

Server-side image maps are rarely used nowadays and for good reason: they make it far more difficult for the user to determine whether or not they are in the right area of the image (the principle of least surprise applies here). They should not be used at all in Section-508-compliant websites unless equivalent text links are provided (which pretty much obviates the image map in the first place).

The test for them, however, is simple enough:

  <tr>
<td>verifyXpathCount</td>
<td>//img[ancestor::a and @ismap]</td>
<td>0</td>
</tr>
<tr>
<td>verifyXpathCount</td>
<td>//input[normalize-space(@type)='image' and @ismap]</td>
<td>0</td>
</tr>

Alt attributes (round 2)

As we said above, the requirement for alt attributes on image tags comes with an exception: where an image alt description would be redundant, use «alt=»». The sort of image envisioned here might, for example, depict a paginating arrow. Even for such images, the alt attribute can be useful unless there is already equivalent text right next to the image. (Of course, if there is such text already, why are you using the image? Use a link instead!)

For the latter case, the solution is necessarily more complex. Selenium cannot be expected to read your mind: you must know what you expect your page to look like. However, it is not difficult to determine whether it does in fact look like what you expect.

We will implement our first Selenium extension to test for this. The Selenium IDE allows you to define a set of extensions in a JavaScript file. By convention, this is called «user-extensions.js», but it can be called anything you like. More than one file can be applied if desired, enabling modularization if it is appropriate for your situation. Apply the files you want using «Options->Selenium Core Extensions» in the «Options» menu in the Selenium IDE.

Our extension, doStoreXPathInfo, is shown below. Selenium will ignore the «do» part of the function name; the command will appear in the Selenium IDE as «storeXPathInfo». It takes two arguments: the XPath expression to be evaluated and the variable within which to store the information retrieved about each node selected by the XPath expression. This variable may be used later in the same Selenium test.

The XPath expression may return elements or attributes, and may return a single node or multiple nodes. The information retrieved depends upon the node: for attributes, the value is retrieved; for elements, the inner HTML is retrieved. If none of these has succeeded, the id attribute is retrieved; failing even this, the name attribute of the node is retrieved. None of these guarantees that information is retrieved; if no information is retrieved about a node, the string «no info» is substituted. Multiple retrieved values are comma-separated.

The function deletes older copies of the variable. For convenient comparison, if no nodes are retrieved, the function stores an empty string as the variable value.

Selenium.prototype.doStoreXPathInfo = function(xpath, variableName)
{
delete storedVars[variableName];
var bbot = this.browserbot;
var result = eval_xpath(xpath, bbot.getDocument(), {
ignoreAttributesWithoutValue: bbot.ignoreAttributesWithoutValue,
allowNativeXpath: bbot.allowNativeXpath,
xpathLibrary: bbot.xpathLibrary,
namespaceResolver: bbot._namespaceResolver
});
if (result.length == 0)
{
this.doEcho('No nodes were found corresponding to the XPath expression ' + xpath);
this.doStore('', variableName);
return;
}
var values = '';
for (var i = 0; i < result.length; ++i)
{
if (values)
{
values += ',';
}
var value = '';
var attributesToBeTried = new Array('value', 'innerHTML', 'id', 'name');
for (var j = 0; j < attributesToBeTried.length && ! value; j++)
{
value = result[i].wrappedJSObject[attributesToBeTried[j]];
}
values += (value ? value : 'no info');
}
if (values)
{
this.doStore(values, variableName);
}
else
{
this.doEcho('No information was retrieved to store to the variable ' + variableName);
}
}

С этим дополнением тест не слишком сложен. Во-первых, не должно быть изображений без
атрибутов
alt :

  <tr>
<td>verifyXpathCount</td>
<td>//img[not(@alt)]</td>
<td>0</td>
</tr>

Во-вторых, вы должны убедиться, что правильные изображения имеют пустые атрибуты alt. Вы
можете просто посчитать количество таких изображений и сравнить с тем, что вы ожидаете:

  <tr>
<td>verifyXpathCount</td>
<td>//img[@alt and not(normalize-space(@alt))]</td>
<td>yourCountHere</td>
</tr>

Of course, you would have to replace «yourCountHere» with the number of images you expect to have empty alt attributes.

However, as you are a responsible developer, you will want to check the source URLs to ensure that only the correct images have empty alt attributes. You, therefore, will use the new command instead:

  <tr>
<td>storeXPathInfo</td>
<td>//img[@alt and not(normalize-space(@alt))]/@src</td>
<td>noAltImages</td>
</tr>
<tr>
<td>verifyEval</td>
<td>'${noAltImages}'=='yourValueHere'</td>
<td>exact:true</td>
</tr>

Replace «yourValueHere» with the value you expect the page to return, that is, the source URLs of the images without the alt attribute, in document order and comma-separated (don’t add extra spaces!). If you don’t expect to have images without the alt attribute, you may, as noted above, use an empty string (e.g., «‘${noAltImages}’==»»), but better yet is not to use this test at all; the test under Alt attribute (round 1) is quite sufficient.

Font size

Font sizes and other sizes that may be changed to assist readers on Section-508-compliant sites should be denominated in ems rather than pixels or points. This enables browser controls to be used to resize the actual appearance of fonts.

The term «em» originally referred, in early typography, to the height of the metal base from which the cast letter arose (it takes its name from the fact that in older typefaces the capital M was frequently made exactly this width). The term has been abstracted to mean «the number of points specifying a size for a particular typeface», so for a 9-point typeface, 1 em would equal 9 points. However, the em scales when the font size is changed by the user, unlike pixels or points, which are fixed in size.

While Selenium may not be the ideal testing tool for this (a simple text search can discover whether there are any appearances of «px» or «pt» in your CSS files), it can still be useful as a backstop. The test requires a somewhat more complex Selenium extension than we saw earlier:

Selenium.prototype.getFontsInPixelsCount = function()
{
var errorList = new Array();
var index = 0;
var errors = 0;
var regex = /[0-9]+[ ]*pt|[0-9]+[ ]*px/i;
var stylesheets = this.browserbot.getDocument().styleSheets;
var propertiesToBeTested = new Array('font', 'fontSize', 'letterSpacing', 'lineHeight', 'textIndent', 'wordSpacing');
for (var i = 0; i < stylesheets.length; i++)
{
var stylesheet = stylesheets[i];
if (! stylesheet.disabled && (! stylesheet.href || stylesheet.href.substring(0, 9) != 'chrome://'))
{
var rules = this.getAllStyleRules(stylesheet.cssRules);
for (var j = 0; j < rules.length; j++)
{
if (rules[j].type == CSSRule.STYLE_RULE)
{
var style = rules[j].style;
for (var k = 0; k < propertiesToBeTested.length; k++)
{
var text = style[propertiesToBeTested[k]];
if (text && regex.test(text))
{
errorList[index++] = (stylesheet.href ? stylesheet.href : 'inline-style') +
'@' + rules[j].selectorText;
errors++;
}
}
}
}
}
}
if (errors)
{
var errorStr = '';
for (var i = 0; i < errorList.length; i++)
{
if (errorStr)
{
errorStr += ',';
}
errorStr += errorList[i];
}
this.doEcho('errors: ' + errorStr);
}
return errors;
}

Selenium.prototype.getAllStyleRules = function(rules)
{
var result = new Array();
var index = 0;
for (var i = 0; i < rules.length; i++)
{
if (rules[i].type == CSSRule.STYLE_RULE)
{
result[index++] = rules[i];
}
else if (rules[i].type == CSSRule.IMPORT_RULE)
{
var inner = this.getAllStyleRules(rules[i].styleSheet.cssRules);
result = result.concat(inner);
index = result.length;
}
}
return result;
}

Это расширение игнорирует таблицы стилей по умолчанию, поставляемые с Firefox, так как они должны использовать шрифты, заданные точками. Опять же, с этим расширением тест довольно прост:

  <tr>
<td>verifyFontsInPixelsCount</td>
<td>0</td>
<td></td>
</tr>

Note that, like our previous extension, Selenium does not directly use the name of the extension function. Because this is a «getXXX» function, Selenium automatically generates the command «verifyFontsInPixelsCount» (also «assertFontsInPixelsCount» and «waitForFontsInPixelsCount»). It also automatically takes care of doing the comparison, marking the command as failed if the count doesn’t match for the verify and assert commands (which differ in that the verify will continue after failure, while the assert will stop), or waiting for the configured maximum time period for the waitFor command (not terribly useful in this particular case).

Conclusion

This review doesn’t by any means fully exhaust Section 508 requirements. There are many other important features that are needed on accessible websites. However, Selenium can be used to test the features covered here easily and there are few if any downsides to accommodating them. There is also no reason not to test new or existing sites for these features or not to add them where it is possible to do so.

Making all your sites available to as wide an audience as possible is easy with Selenium!