Skip to content

Commit

Permalink
Verify in tests that alt="" does not contain element type.
Browse files Browse the repository at this point in the history
  • Loading branch information
isoos committed Dec 12, 2023
1 parent 06736a2 commit cbc2835
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 15 deletions.
2 changes: 2 additions & 0 deletions app/lib/frontend/dom/dom.dart
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,8 @@ class Image {

Image({
required this.src,

/// Note: the `alt` text of elements should not include the element type (e.g. `icon`).
required this.alt,
required this.width,
required this.height,
Expand Down
31 changes: 16 additions & 15 deletions pkg/_pub_shared/lib/validation/html/html_validation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,38 +29,37 @@ void parseAndValidateHtml(String html) {
/// - `<script> tags have no `src` attribute or have content (except `ld+json`
/// meta content).
void validateHtml(Node root) {
List<Element> elements;
List<Element> links;
List<Element> scripts;
List<Element> buttons;

late List<Element> Function(String selector) querySelectorAll;
if (root is DocumentFragment) {
elements = root.querySelectorAll('*');
links = root.querySelectorAll('a');
scripts = root.querySelectorAll('script');
buttons = root.querySelectorAll('button');
querySelectorAll = root.querySelectorAll;
} else if (root is Document) {
_validateCanonicalLink(root.querySelector('head')!);
elements = root.querySelectorAll('*');
links = root.querySelectorAll('a');
scripts = root.querySelectorAll('script');
buttons = root.querySelectorAll('button');
querySelectorAll = root.querySelectorAll;
} else {
throw AssertionError('Unknown html element type: $root');
}

// No inline JS attribute
for (Element elem in elements) {
for (final elem in querySelectorAll('*')) {
// No inline JS attribute
for (final attr in elem.attributes.keys) {
final name = attr.toString();
if (name.toLowerCase().startsWith('on')) {
throw AssertionError(
'No inline JS attribute is allowed, found: ${elem.outerHtml}.');
}
}
// alt attribute should not contain the element type
final alt = elem.attributes['alt']?.toLowerCase();
if (alt != null) {
if (alt.split(' ').contains('icon')) {
throw AssertionError(
'"alt" attribute should not contain the type of the elemnt: ${elem.outerHtml}');
}
}
}

// All <a target="_blank"> links must have rel="noopener"
final links = querySelectorAll('a');
for (Element elem in links) {
if (elem.attributes['target'] == '_blank') {
if (!elem.attributes.containsKey('rel')) {
Expand Down Expand Up @@ -97,6 +96,7 @@ void validateHtml(Node root) {
}

// No inline script tag.
final scripts = querySelectorAll('script');
for (Element elem in scripts) {
if (elem.attributes['type'] == 'application/ld+json') {
if (elem.attributes.length != 1) {
Expand Down Expand Up @@ -130,6 +130,7 @@ void validateHtml(Node root) {
}

// Lighthouse flags buttons that don't have text content or an `aria-label` property.
final buttons = querySelectorAll('button');
for (final elem in buttons) {
final text = elem.attributes['aria-label']?.trim() ?? elem.text.trim();
if (text.isEmpty) {
Expand Down

0 comments on commit cbc2835

Please sign in to comment.