editoria11y-1.0.0-alpha8/src/TestNames.php
src/TestNames.php
<?php
namespace Drupal\editoria11y;
/**
* Provides various reusable strings.
*
* @phpstan-consistent-constructor
*/
class TestNames {
/**
* Legacy name conversions.
*/
public static function oldNames() {
return [
'altDeadspace' => 'ALT_UNPRONOUNCEABLE',
'altEmptyLinked' => 'LINK_IMAGE_NO_ALT_TEXT',
'altImageOf' => 'SUS_ALT',
'altImageOfLinked' => 'LINK_SUS_ALT',
'altLong' => 'IMAGE_ALT_TOO_LONG',
'altLongLinked' => 'LINK_IMAGE_LONG_ALT',
'altMeaningless' => 'ALT_PLACEHOLDER',
'altMeaninglessLinked' => 'LINK_PLACEHOLDER_ALT',
'altMissing' => 'MISSING_ALT',
'altNull' => 'IMAGE_DECORATIVE',
'altPartOfLinkWithText' => 'LINK_IMAGE_ALT_AND_TEXT',
'altURL' => 'ALT_FILE_EXT',
'altURLLinked' => 'LINK_ALT_FILE_EXT',
'blockquoteIsShort' => 'QA_BLOCKQUOTE',
'embedAudio' => 'EMBED_AUDIO',
'embedCustom' => 'EMBED_GENERAL',
'embedVideo' => 'EMBED_VIDEO',
'embedVisualization' => 'EMBED_DATA_VIZ',
'headingEmpty' => 'HEADING_EMPTY',
'headingIsLong' => 'HEADING_LONG',
'headingLevelSkipped' => 'HEADING_SKIPPED_LEVEL',
'linkDocument' => 'QA_PDF',
'linkNewWindow' => 'LINK_NEW_TAB',
'linkNoLabel' => 'LINK_EMPTY_NO_LABEL',
'linkNoText' => 'LINK_EMPTY',
'linkTextIsGeneric' => 'LINK_STOPWORD',
'linkTextIsURL' => 'LINK_URL',
'tableContainsContentHeading' => 'TABLES_SEMANTIC_HEADING',
'tableEmptyHeaderCell' => 'TABLES_EMPTY_HEADING',
'tableNoHeaderCells' => 'TABLES_MISSING_HEADINGS',
'textPossibleHeading' => 'QA_FAKE_HEADING',
'textPossibleList' => 'QA_FAKE_LIST',
'textUppercase' => 'QA_UPPERCASE',
// @todo but adopt Adam's wording
// And 'MISSING_ALT_LINKED'?
// New.
// QA_DOCUMENT is new and closer.
];
}
/**
* Returns key/value pairs of known test names.
*/
public static function coreNames(): array {
return [
'ALT_FILE_EXT' => t("Image's text alternative is a URL"),
'ALT_MAYBE_BAD' => t('Manual check: alt text may be meaningless'),
'ALT_PLACEHOLDER' => t('Alt text is meaningless'),
'ALT_UNPRONOUNCEABLE' => t("Image's text alternative is unpronounceable"),
'EMBED_AUDIO' => t('Manual check: is an accurate transcript provided?'),
'EMBED_CUSTOM' => t('Manual check: is this embedded content accessible?'),
'EMBED_DATA_VIZ' => t('Manual check: is this visualization accessible?'),
'EMBED_GENERAL' => t('Manual check: is this embedded content accessible?'),
'EMBED_MISSING_TITLE' => t('Frame missing title attribute'),
'EMBED_VIDEO' => t('Manual check: is this video accurately captioned?'),
'HEADING_EMPTY' => t('Heading tag without any text'),
'HEADING_EMPTY_WITH_IMAGE' => t('Heading has no text, but contains an image'),
'HEADING_LONG' => t('Manual check: long heading'),
'HEADING_SKIPPED_LEVEL' => t('Manual check: was a heading level skipped?'),
'IMAGE_ALT_TOO_LONG' => t('Manual check: very long alternative text'),
'IMAGE_DECORATIVE' => t('Manual check: image has no alt text'),
'LINK_ALT_FILE_EXT' => t("Linked image's text alternative is a URL"),
'LINK_ALT_MAYBE_BAD' => t('Manual check: linked alt text may be meaningless'),
'LINK_ALT_UNPRONOUNCEABLE' => t('Alt text in linked image is unpronounceable.'),
'LINK_DOI' => t('APA Style guide recommends using descriptive DOI links'),
'LINK_EMPTY' => t('Link with no accessible text'),
'LINK_EMPTY_NO_LABEL' => t('Link with no accessible label'),
'LINK_IMAGE_ALT_AND_TEXT' => t('Manual check: link contains both text and an image'),
'LINK_IMAGE_LONG_ALT' => t('Image has no alternative text attribute'),
'LINK_IMAGE_NO_ALT_TEXT' => t('Linked image has no alt text'),
'LINK_IMAGE_TEXT' => t('Manual check: Image in a link with text is marked as decorative.'), // @Todo turn off?
'LINK_NEW_TAB' => t('Manual check: is opening a new window expected?'),
'LINK_PLACEHOLDER_ALT' => t('Linked alt text is meaningless'),
'LINK_STOPWORD' => t('Manual check: is this link meaningful and concise?'),
'LINK_SUS_ALT' => t('Manual check: possibly redundant text in linked image'),
'LINK_SYMBOLS' => t('Manual check: are the symbols or emoji in this link meaningful?'),
'LINK_URL' => t('Manual check: is this link text a URL?'),
'MISSING_ALT' => t('Image has no alternative text attribute'),
'MISSING_ALT_LINK' => t('Linked image has no alternative text attribute'),
'MISSING_ALT_LINK_HAS_TEXT' => t('Image in link with text has no alternative text attribute'),
'QA_BLOCKQUOTE' => t('Manual check: is this a blockquote?'),
'QA_DOCUMENT' => t('Manual check: linked document'),
'QA_FAKE_HEADING' => t('Manual check: should this be a heading?'),
'QA_FAKE_LIST' => t('Manual check: should this have list formatting?'),
'QA_IN_PAGE_LINK' => t('Broken same-page link'),
'QA_JUSTIFY' => t('Justified text'),
'QA_PDF' => t('Manual check: is the linked document accessible?'),
'QA_STRONG_ITALICS' => t('Manual check: entire paragraph is emphasized'),
'QA_UNDERLINE' => t('Underlined text'),
'QA_UPPERCASE' => t('Manual check: is this uppercase text needed?'),
'SUS_ALT' => t('Manual check: possibly redundant text in alt'),
'TABLES_EMPTY_HEADING' => t('Empty table header cell'),
'TABLES_MISSING_HEADINGS' => t('Table has no header cells'),
'TABLES_SEMANTIC_HEADING' => t('Content heading inside a table'),
'HEADING_MISSING_ONE' => t('Missing Heading 1'),
'HEADING_FIRST' => t('The first heading on a page should usually be a Heading 1 or Heading 2'),
'IMAGE_FIGURE_DECORATIVE' => t('Manual check: image in a figure marked as decorative'),
'IMAGE_FIGURE_DUPLICATE_ALT' => t('Alt is the same as caption text'),
'IMAGE_DECORATIVE_CAROUSEL' => t('Image in a carousel or gallery marked as decorative'), // @Todo: move to content?
'QA_NESTED_COMPONENTS' => t('Nested interactive layout components'),
'QA_SMALL_TEXT' => t('Small text'),
'QA_SUBSCRIPT' => t('Manual check: use of subscript or superscript as visual formatting'),
'BTN_EMPTY' => t('Button purpose is not machine-readable'),
'BTN_EMPTY_LABELLEDBY' => t('Button has an invalid ARIA label'),
'BTN_ROLE_IN_NAME' => t('Button name repeats the word "button"'),
'LINK_EMPTY_LABELLEDBY' => t('Link invalid aria-labelledby attribute'),
'LINK_FILE_EXT' => t('Link points to a file without warning'),
'LINK_IDENTICAL_NAME' => t('Manual check: link has identical text as another link but points to a different page'),
'LINK_STOPWORD_ARIA' => t('Manual check: link text overridden by ARIA that may not be meaningful'),
'LINK_CLICK_HERE' => t('Manual check: link contains "click here"'),
'LINK_IMAGE_ALT' => t('Manual check: does the linked image alt describe the destination or action?'),
'QA_BAD_LINK' => t('Manual check: link target may be invalid'),
'DUPLICATE_ID' => t('Manual check: duplicate ID'),
'DUPLICATE_TITLE' => t('Duplicate title attribute'),
'LABEL_IN_NAME' => t('Visible name different than machine-readable name'),
'LABELS_ARIA_LABEL_INPUT' => t('Manual check: is there a visible label for this field?'),
'CONTRAST_ERROR' => t('Text does not have enough contrast to be easily legible'),
'CONTRAST_ERROR_GRAPHIC' => t('Graphic or icon does not have enough contrast with the background'),
'CONTRAST_INPUT' => t('Input does not provide enough contrast to be easily legible'),
'CONTRAST_PLACEHOLDER' => t('Placeholder text does not have enough contrast to be easily legible'),
'CONTRAST_PLACEHOLDER_UNSUPPORTED' => t('Manual check: does this placeholder text have enough contrast?'),
'CONTRAST_WARNING' => t('Manual check: does this text have enough contrast?'),
'CONTRAST_WARNING_GRAPHIC' => t('Manual check: does this graphic or icon have enough contrast?'),
'HIDDEN_FOCUSABLE' => t('Screen readers told not to speak the name of an interactive element'),
'EMBED_UNFOCUSABLE' => t('Frame with tabindex="-1" will not be keyboard accessible.'),
'TABINDEX_ATTR' => t('Provided tabindex value removes element from reading order'),
'UNCONTAINED_LI' => t('Invalid HTML list'),
'META_LANG' => t('Meta tag for page language missing'),
'META_MAX' => t('Meta tag sets max user scaling'),
'META_REFRESH' => t('Meta tag automatically refreshes page'),
'META_SCALABLE' => t('Meta tag prevents user scaling'),
'META_TITLE' => t('Meta tag for page title missing'),
];
}
/**
* Returns list of tests defined as content.
*/
public static function contentTests(): array {
return [
'ALT_FILE_EXT',
'ALT_MAYBE_BAD',
'ALT_PLACEHOLDER',
'ALT_UNPRONOUNCEABLE',
'EMBED_AUDIO',
'EMBED_CUSTOM',
'EMBED_DATA_VIZ',
'EMBED_GENERAL',
'EMBED_MISSING_TITLE',
'EMBED_VIDEO',
'HEADING_EMPTY',
'HEADING_EMPTY_WITH_IMAGE',
'HEADING_LONG',
'HEADING_SKIPPED_LEVEL',
'IMAGE_ALT_TOO_LONG',
'IMAGE_DECORATIVE',
'LINK_ALT_FILE_EXT',
'LINK_ALT_MAYBE_BAD',
'LINK_ALT_UNPRONOUNCEABLE',
'LINK_DOI',
'LINK_EMPTY',
'LINK_EMPTY_NO_LABEL',
'LINK_IMAGE_ALT_AND_TEXT',
'LINK_IMAGE_LONG_ALT',
'LINK_IMAGE_NO_ALT_TEXT',
'LINK_IMAGE_TEXT', // @Todo turn off?
'LINK_NEW_TAB',
'LINK_PLACEHOLDER_ALT',
'LINK_STOPWORD',
'LINK_SUS_ALT',
'LINK_SYMBOLS',
'LINK_URL',
'MISSING_ALT',
'MISSING_ALT_LINK',
'MISSING_ALT_LINK_HAS_TEXT',
'QA_BLOCKQUOTE',
'QA_DOCUMENT',
'QA_FAKE_HEADING',
'QA_FAKE_LIST',
'QA_IN_PAGE_LINK',
'QA_JUSTIFY',
'QA_PDF',
'QA_STRONG_ITALICS',
'QA_UNDERLINE',
'QA_UPPERCASE',
'SUS_ALT',
'TABLES_EMPTY_HEADING',
'TABLES_MISSING_HEADINGS',
'TABLES_SEMANTIC_HEADING',
];
}
public static function contentTestNames(): array {
return array_intersect_key(self::coreNames(), array_flip(self::contentTests()));
}
public static function devTestNames(): array {
return array_diff(self::coreNames(), self::contentTestNames());
}
public static function offByDefault(): array {
return [
'CONTRAST_WARNING_GRAPHIC',
'LINK_CLICK_HERE',
'LINK_IMAGE_ALT',
];
}
/**
* Filters to tests with results in the DB, and adds custom tests.
*
* @param string $type
* Request 'results' or 'dismissals'.
*/
public function activeNames(string $type = 'results'): array {
$staticTests = $this->coreNames();
$names = [];
$database = \Drupal::database();
// $table = $type === 'results' ? 'editoria11y_results' : 'editoria11y_dismissals';
if ($type == 'results') {
$results = $database->select('editoria11y_results', 't')
->fields('t', ['result_key', 'result_name'])
->groupBy('result_key')
->groupBy('result_name')
->orderBy('result_name')
->execute();
}
else {
$results = $database->select('editoria11y_dismissals', 't')
->fields('t', ['result_key', 'result_name'])
->groupBy('result_key')
->groupBy('result_name')
->orderBy('result_name')
->execute();
}
foreach ($results as $result) {
if (!in_array($result->result_key, $names)) {
if (in_array($result->result_key, $staticTests)) {
// Translatable value from known test.
$names[$result->result_key] = $staticTests[$result->result_key];
}
else {
// Raw value from custom test.
$names[$result->result_key] = $result->result_name;
}
}
}
return $names;
}
}
