axe-core 4.10.2-canary.f2b535a → 4.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/axe.d.ts CHANGED
@@ -342,9 +342,6 @@ declare namespace axe {
342
342
  interface DqElement extends SerialDqElement {
343
343
  element: Element;
344
344
  toJSON(): SerialDqElement;
345
- }
346
- interface DqElementConstructor {
347
- new (elm: Element, options?: { absolutePaths?: boolean }): DqElement;
348
345
  mergeSpecs(
349
346
  childSpec: SerialDqElement,
350
347
  parentSpec: SerialDqElement
@@ -408,24 +405,6 @@ declare namespace axe {
408
405
  boundingClientRect: DOMRect;
409
406
  }
410
407
 
411
- interface CustomNodeSerializer<T = SerialDqElement> {
412
- toSpec: (dqElm: DqElement) => T;
413
- mergeSpecs: (nodeSpec: T, parentFrameSpec: T) => T;
414
- }
415
-
416
- interface NodeSerializer {
417
- update: <T>(serializer: CustomNodeSerializer<T>) => void;
418
- toSpec: (node: Element | VirtualNode) => SerialDqElement;
419
- dqElmToSpec: (
420
- dqElm: DqElement | SerialDqElement,
421
- options?: RunOptions
422
- ) => SerialDqElement;
423
- mergeSpecs: (
424
- nodeSpec: SerialDqElement,
425
- parentFrameSpec: SerialDqElement
426
- ) => SerialDqElement;
427
- }
428
-
429
408
  interface Utils {
430
409
  getFrameContexts: (
431
410
  context?: ElementContext,
@@ -444,13 +423,15 @@ declare namespace axe {
444
423
  selector: unknown
445
424
  ) => selector is LabelledShadowDomSelector;
446
425
 
447
- DqElement: DqElementConstructor;
426
+ DqElement: new (
427
+ elm: Element,
428
+ options?: { absolutePaths?: boolean }
429
+ ) => DqElement;
448
430
  uuid: (
449
431
  options?: { random?: Uint8Array | Array<number> },
450
432
  buf?: Uint8Array | Array<number>,
451
433
  offset?: number
452
434
  ) => string | Uint8Array | Array<number>;
453
- nodeSerializer: NodeSerializer;
454
435
  }
455
436
 
456
437
  interface Aria {
package/axe.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! axe v4.10.2-canary.f2b535a
1
+ /*! axe v4.10.3
2
2
  * Copyright (c) 2015 - 2025 Deque Systems, Inc.
3
3
  *
4
4
  * Your use of this Source Code Form is subject to the terms of the Mozilla Public
@@ -22,7 +22,7 @@
22
22
  }, _typeof(o);
23
23
  }
24
24
  var axe = axe || {};
25
- axe.version = '4.10.2-canary.f2b535a';
25
+ axe.version = '4.10.3';
26
26
  if (typeof define === 'function' && define.amd) {
27
27
  define('axe-core', [], function() {
28
28
  return axe;
@@ -11255,7 +11255,7 @@
11255
11255
  superclassRole: [ 'composite' ]
11256
11256
  },
11257
11257
  tabpanel: {
11258
- type: 'widget',
11258
+ type: 'structure',
11259
11259
  allowedAttrs: [ 'aria-expanded' ],
11260
11260
  superclassRole: [ 'section' ],
11261
11261
  accessibleNameRequired: false
@@ -19507,8 +19507,14 @@
19507
19507
  }
19508
19508
  function preloadMedia(_ref74) {
19509
19509
  var _ref74$treeRoot = _ref74.treeRoot, treeRoot = _ref74$treeRoot === void 0 ? axe._tree[0] : _ref74$treeRoot;
19510
- var mediaVirtualNodes = query_selector_all_filter_default(treeRoot, 'video, audio', function(_ref75) {
19510
+ var mediaVirtualNodes = query_selector_all_filter_default(treeRoot, 'video[autoplay], audio[autoplay]', function(_ref75) {
19511
19511
  var actualNode = _ref75.actualNode;
19512
+ if (actualNode.preload === 'none' && actualNode.readyState === 0 && actualNode.networkState !== actualNode.NETWORK_LOADING) {
19513
+ return false;
19514
+ }
19515
+ if (actualNode.hasAttribute('paused') || actualNode.hasAttribute('muted')) {
19516
+ return false;
19517
+ }
19512
19518
  if (actualNode.hasAttribute('src')) {
19513
19519
  return !!actualNode.getAttribute('src');
19514
19520
  }
@@ -20511,6 +20517,54 @@
20511
20517
  return out ? true : void 0;
20512
20518
  }
20513
20519
  var th_has_data_cells_evaluate_default = thHasDataCellsEvaluate;
20520
+ function tdHeadersAttrEvaluate(node) {
20521
+ var cells = [];
20522
+ var reviewCells = [];
20523
+ var badCells = [];
20524
+ for (var rowIndex = 0; rowIndex < node.rows.length; rowIndex++) {
20525
+ var row = node.rows[rowIndex];
20526
+ for (var cellIndex = 0; cellIndex < row.cells.length; cellIndex++) {
20527
+ cells.push(row.cells[cellIndex]);
20528
+ }
20529
+ }
20530
+ var ids = cells.filter(function(cell) {
20531
+ return cell.getAttribute('id');
20532
+ }).map(function(cell) {
20533
+ return cell.getAttribute('id');
20534
+ });
20535
+ cells.forEach(function(cell) {
20536
+ var isSelf = false;
20537
+ var notOfTable = false;
20538
+ if (!cell.hasAttribute('headers') || !_isVisibleToScreenReaders(cell)) {
20539
+ return;
20540
+ }
20541
+ var headersAttr = cell.getAttribute('headers').trim();
20542
+ if (!headersAttr) {
20543
+ return reviewCells.push(cell);
20544
+ }
20545
+ var headers = token_list_default(headersAttr);
20546
+ if (headers.length !== 0) {
20547
+ if (cell.getAttribute('id')) {
20548
+ isSelf = headers.indexOf(cell.getAttribute('id').trim()) !== -1;
20549
+ }
20550
+ notOfTable = headers.some(function(header) {
20551
+ return !ids.includes(header);
20552
+ });
20553
+ if (isSelf || notOfTable) {
20554
+ badCells.push(cell);
20555
+ }
20556
+ }
20557
+ });
20558
+ if (badCells.length > 0) {
20559
+ this.relatedNodes(badCells);
20560
+ return false;
20561
+ }
20562
+ if (reviewCells.length) {
20563
+ this.relatedNodes(reviewCells);
20564
+ return void 0;
20565
+ }
20566
+ return true;
20567
+ }
20514
20568
  var aria_exports = {};
20515
20569
  __export(aria_exports, {
20516
20570
  allowedAttr: function allowedAttr() {
@@ -22732,66 +22786,6 @@
22732
22786
  return !!attrDefinition;
22733
22787
  }
22734
22788
  var validate_attr_default = validateAttr;
22735
- var messageKeys = [ 'cell-header-not-in-table', 'cell-header-not-th', 'header-refs-self', 'empty-hdrs' ];
22736
- var notInTable = messageKeys[0], notTh = messageKeys[1], selfRef = messageKeys[2], emptyHdrs = messageKeys[3];
22737
- function tdHeadersAttrEvaluate(node) {
22738
- var cells = [];
22739
- var cellRoleById = {};
22740
- for (var rowIndex = 0; rowIndex < node.rows.length; rowIndex++) {
22741
- var row = node.rows[rowIndex];
22742
- for (var cellIndex = 0; cellIndex < row.cells.length; cellIndex++) {
22743
- var cell = row.cells[cellIndex];
22744
- cells.push(cell);
22745
- var cellId = cell.getAttribute('id');
22746
- if (cellId) {
22747
- cellRoleById[cellId] = get_role_default(cell);
22748
- }
22749
- }
22750
- }
22751
- var badCells = _defineProperty(_defineProperty(_defineProperty(_defineProperty({}, selfRef, new Set()), notInTable, new Set()), notTh, new Set()), emptyHdrs, new Set());
22752
- cells.forEach(function(cell) {
22753
- if (!cell.hasAttribute('headers') || !_isVisibleToScreenReaders(cell)) {
22754
- return;
22755
- }
22756
- var headersAttr = cell.getAttribute('headers').trim();
22757
- if (!headersAttr) {
22758
- badCells[emptyHdrs].add(cell);
22759
- return;
22760
- }
22761
- var cellId = cell.getAttribute('id');
22762
- var headers = token_list_default(headersAttr);
22763
- headers.forEach(function(headerId) {
22764
- if (cellId && headerId === cellId) {
22765
- badCells[selfRef].add(cell);
22766
- } else if (!cellRoleById[headerId]) {
22767
- badCells[notInTable].add(cell);
22768
- } else if (![ 'columnheader', 'rowheader' ].includes(cellRoleById[headerId])) {
22769
- badCells[notTh].add(cell);
22770
- }
22771
- });
22772
- });
22773
- var _iterator15 = _createForOfIteratorHelper(messageKeys), _step15;
22774
- try {
22775
- for (_iterator15.s(); !(_step15 = _iterator15.n()).done; ) {
22776
- var messageKey = _step15.value;
22777
- if (badCells[messageKey].size > 0) {
22778
- this.relatedNodes(_toConsumableArray(badCells[messageKey]));
22779
- if (messageKey === emptyHdrs) {
22780
- return void 0;
22781
- }
22782
- this.data({
22783
- messageKey: messageKey
22784
- });
22785
- return false;
22786
- }
22787
- }
22788
- } catch (err) {
22789
- _iterator15.e(err);
22790
- } finally {
22791
- _iterator15.f();
22792
- }
22793
- return true;
22794
- }
22795
22789
  function tdHasHeaderEvaluate(node) {
22796
22790
  var badCells = [];
22797
22791
  var cells = get_all_cells_default(node);
@@ -23180,19 +23174,19 @@
23180
23174
  return;
23181
23175
  }
23182
23176
  var frameAncestry = r.node.ancestry.slice(0, -1);
23183
- var _iterator16 = _createForOfIteratorHelper(iframeResults), _step16;
23177
+ var _iterator15 = _createForOfIteratorHelper(iframeResults), _step15;
23184
23178
  try {
23185
- for (_iterator16.s(); !(_step16 = _iterator16.n()).done; ) {
23186
- var iframeResult = _step16.value;
23179
+ for (_iterator15.s(); !(_step15 = _iterator15.n()).done; ) {
23180
+ var iframeResult = _step15.value;
23187
23181
  if (_matchAncestry(frameAncestry, iframeResult.node.ancestry)) {
23188
23182
  r.result = iframeResult.result;
23189
23183
  break;
23190
23184
  }
23191
23185
  }
23192
23186
  } catch (err) {
23193
- _iterator16.e(err);
23187
+ _iterator15.e(err);
23194
23188
  } finally {
23195
- _iterator16.f();
23189
+ _iterator15.f();
23196
23190
  }
23197
23191
  });
23198
23192
  iframeResults.forEach(function(r) {
@@ -23679,11 +23673,11 @@
23679
23673
  }
23680
23674
  function getShadowColorsMap(parsedShadows) {
23681
23675
  var colorMap = {};
23682
- var _iterator17 = _createForOfIteratorHelper(parsedShadows), _step17;
23676
+ var _iterator16 = _createForOfIteratorHelper(parsedShadows), _step16;
23683
23677
  try {
23684
- for (_iterator17.s(); !(_step17 = _iterator17.n()).done; ) {
23678
+ for (_iterator16.s(); !(_step16 = _iterator16.n()).done; ) {
23685
23679
  var _colorMap$colorStr;
23686
- var _step17$value = _step17.value, colorStr = _step17$value.colorStr, pixels = _step17$value.pixels;
23680
+ var _step16$value = _step16.value, colorStr = _step16$value.colorStr, pixels = _step16$value.pixels;
23687
23681
  (_colorMap$colorStr = colorMap[colorStr]) !== null && _colorMap$colorStr !== void 0 ? _colorMap$colorStr : colorMap[colorStr] = {
23688
23682
  top: [],
23689
23683
  right: [],
@@ -23704,9 +23698,9 @@
23704
23698
  }
23705
23699
  }
23706
23700
  } catch (err) {
23707
- _iterator17.e(err);
23701
+ _iterator16.e(err);
23708
23702
  } finally {
23709
- _iterator17.f();
23703
+ _iterator16.f();
23710
23704
  }
23711
23705
  return colorMap;
23712
23706
  }
@@ -23783,10 +23777,10 @@
23783
23777
  assert_default(isNaN(fontSize) === false, 'Unable to determine font-size value '.concat(fontSizeStr));
23784
23778
  var thinShadows = [];
23785
23779
  var shadows = _parseTextShadows(textShadow);
23786
- var _iterator18 = _createForOfIteratorHelper(shadows), _step18;
23780
+ var _iterator17 = _createForOfIteratorHelper(shadows), _step17;
23787
23781
  try {
23788
- for (_iterator18.s(); !(_step18 = _iterator18.n()).done; ) {
23789
- var shadow = _step18.value;
23782
+ for (_iterator17.s(); !(_step17 = _iterator17.n()).done; ) {
23783
+ var shadow = _step17.value;
23790
23784
  var colorStr = shadow.colorStr || style.getPropertyValue('color');
23791
23785
  var _shadow$pixels = _slicedToArray(shadow.pixels, 3), offsetX = _shadow$pixels[0], offsetY = _shadow$pixels[1], _shadow$pixels$ = _shadow$pixels[2], blurRadius = _shadow$pixels$ === void 0 ? 0 : _shadow$pixels$;
23792
23786
  if (maxRatio && blurRadius >= fontSize * maxRatio) {
@@ -23819,9 +23813,9 @@
23819
23813
  shadowColors.push(_color3);
23820
23814
  }
23821
23815
  } catch (err) {
23822
- _iterator18.e(err);
23816
+ _iterator17.e(err);
23823
23817
  } finally {
23824
- _iterator18.f();
23818
+ _iterator17.f();
23825
23819
  }
23826
23820
  if (thinShadows.length > 0) {
23827
23821
  var strokeColors = _getStrokeColorsFromShadows(thinShadows, {
@@ -24149,11 +24143,11 @@
24149
24143
  return fgColor;
24150
24144
  }
24151
24145
  function findNodeInContexts(contexts, node) {
24152
- var _iterator19 = _createForOfIteratorHelper(contexts), _step19;
24146
+ var _iterator18 = _createForOfIteratorHelper(contexts), _step18;
24153
24147
  try {
24154
- for (_iterator19.s(); !(_step19 = _iterator19.n()).done; ) {
24148
+ for (_iterator18.s(); !(_step18 = _iterator18.n()).done; ) {
24155
24149
  var _context$vNode;
24156
- var context = _step19.value;
24150
+ var context = _step18.value;
24157
24151
  if (((_context$vNode = context.vNode) === null || _context$vNode === void 0 ? void 0 : _context$vNode.actualNode) === node) {
24158
24152
  return context;
24159
24153
  }
@@ -24163,9 +24157,9 @@
24163
24157
  }
24164
24158
  }
24165
24159
  } catch (err) {
24166
- _iterator19.e(err);
24160
+ _iterator18.e(err);
24167
24161
  } finally {
24168
- _iterator19.f();
24162
+ _iterator18.f();
24169
24163
  }
24170
24164
  }
24171
24165
  function hasValidContrastRatio(bg, fg, fontSize, isBold) {
@@ -24509,10 +24503,10 @@
24509
24503
  function filterByElmsOverlap(vNode, nearbyElms) {
24510
24504
  var fullyObscuringElms = [];
24511
24505
  var partialObscuringElms = [];
24512
- var _iterator20 = _createForOfIteratorHelper(nearbyElms), _step20;
24506
+ var _iterator19 = _createForOfIteratorHelper(nearbyElms), _step19;
24513
24507
  try {
24514
- for (_iterator20.s(); !(_step20 = _iterator20.n()).done; ) {
24515
- var vNeighbor = _step20.value;
24508
+ for (_iterator19.s(); !(_step19 = _iterator19.n()).done; ) {
24509
+ var vNeighbor = _step19.value;
24516
24510
  if (!isDescendantNotInTabOrder2(vNode, vNeighbor) && _hasVisualOverlap(vNode, vNeighbor) && getCssPointerEvents(vNeighbor) !== 'none') {
24517
24511
  if (isEnclosedRect2(vNode, vNeighbor)) {
24518
24512
  fullyObscuringElms.push(vNeighbor);
@@ -24522,9 +24516,9 @@
24522
24516
  }
24523
24517
  }
24524
24518
  } catch (err) {
24525
- _iterator20.e(err);
24519
+ _iterator19.e(err);
24526
24520
  } finally {
24527
- _iterator20.f();
24521
+ _iterator19.f();
24528
24522
  }
24529
24523
  return {
24530
24524
  fullyObscuringElms: fullyObscuringElms,
@@ -24597,10 +24591,10 @@
24597
24591
  }
24598
24592
  var closeNeighbors = [];
24599
24593
  var closestOffset = minOffset;
24600
- var _iterator21 = _createForOfIteratorHelper(_findNearbyElms(vNode, minOffset)), _step21;
24594
+ var _iterator20 = _createForOfIteratorHelper(_findNearbyElms(vNode, minOffset)), _step20;
24601
24595
  try {
24602
- for (_iterator21.s(); !(_step21 = _iterator21.n()).done; ) {
24603
- var vNeighbor = _step21.value;
24596
+ for (_iterator20.s(); !(_step20 = _iterator20.n()).done; ) {
24597
+ var vNeighbor = _step20.value;
24604
24598
  if (get_role_type_default(vNeighbor) !== 'widget' || !_isFocusable(vNeighbor)) {
24605
24599
  continue;
24606
24600
  }
@@ -24629,9 +24623,9 @@
24629
24623
  closeNeighbors.push(vNeighbor);
24630
24624
  }
24631
24625
  } catch (err) {
24632
- _iterator21.e(err);
24626
+ _iterator20.e(err);
24633
24627
  } finally {
24634
- _iterator21.f();
24628
+ _iterator20.f();
24635
24629
  }
24636
24630
  if (closeNeighbors.length === 0) {
24637
24631
  this.data({
@@ -24876,16 +24870,20 @@
24876
24870
  }
24877
24871
  var css_orientation_lock_evaluate_default = cssOrientationLockEvaluate;
24878
24872
  function noAutoplayAudioEvaluate(node, options) {
24873
+ var hasControls = node.hasAttribute('controls');
24874
+ if (node.hasAttribute('loop')) {
24875
+ return hasControls;
24876
+ }
24879
24877
  if (!node.duration) {
24880
24878
  console.warn('axe.utils.preloadMedia did not load metadata');
24881
24879
  return void 0;
24882
24880
  }
24883
24881
  var _options$allowedDurat = options.allowedDuration, allowedDuration = _options$allowedDurat === void 0 ? 3 : _options$allowedDurat;
24884
24882
  var playableDuration = getPlayableDuration(node);
24885
- if (playableDuration <= allowedDuration && !node.hasAttribute('loop')) {
24883
+ if (playableDuration <= allowedDuration) {
24886
24884
  return true;
24887
24885
  }
24888
- if (!node.hasAttribute('controls')) {
24886
+ if (!hasControls) {
24889
24887
  return false;
24890
24888
  }
24891
24889
  return true;
@@ -26120,7 +26118,8 @@
26120
26118
  navigation: true,
26121
26119
  region: true,
26122
26120
  search: false,
26123
- status: true
26121
+ status: true,
26122
+ tabpanel: true
26124
26123
  };
26125
26124
  function validScrollableTagName(node) {
26126
26125
  var nodeName2 = node.nodeName.toUpperCase();
@@ -26833,18 +26832,18 @@
26833
26832
  if (Array.isArray(options[role])) {
26834
26833
  allowed = unique_array_default(options[role].concat(allowed));
26835
26834
  }
26836
- var _iterator22 = _createForOfIteratorHelper(virtualNode.attrNames), _step22;
26835
+ var _iterator21 = _createForOfIteratorHelper(virtualNode.attrNames), _step21;
26837
26836
  try {
26838
- for (_iterator22.s(); !(_step22 = _iterator22.n()).done; ) {
26839
- var attrName = _step22.value;
26837
+ for (_iterator21.s(); !(_step21 = _iterator21.n()).done; ) {
26838
+ var attrName = _step21.value;
26840
26839
  if (validate_attr_default(attrName) && !allowed.includes(attrName) && !ignoredAttrs(attrName, virtualNode.attr(attrName), virtualNode)) {
26841
26840
  invalid.push(attrName);
26842
26841
  }
26843
26842
  }
26844
26843
  } catch (err) {
26845
- _iterator22.e(err);
26844
+ _iterator21.e(err);
26846
26845
  } finally {
26847
- _iterator22.f();
26846
+ _iterator21.f();
26848
26847
  }
26849
26848
  if (!invalid.length) {
26850
26849
  return true;
@@ -28206,10 +28205,10 @@
28206
28205
  value: function setAllowedOrigins(allowedOrigins) {
28207
28206
  var defaultOrigin = getDefaultOrigin();
28208
28207
  this.allowedOrigins = [];
28209
- var _iterator23 = _createForOfIteratorHelper(allowedOrigins), _step23;
28208
+ var _iterator22 = _createForOfIteratorHelper(allowedOrigins), _step22;
28210
28209
  try {
28211
- for (_iterator23.s(); !(_step23 = _iterator23.n()).done; ) {
28212
- var origin = _step23.value;
28210
+ for (_iterator22.s(); !(_step22 = _iterator22.n()).done; ) {
28211
+ var origin = _step22.value;
28213
28212
  if (origin === constants_default.allOrigins) {
28214
28213
  this.allowedOrigins = [ '*' ];
28215
28214
  return;
@@ -28220,9 +28219,9 @@
28220
28219
  }
28221
28220
  }
28222
28221
  } catch (err) {
28223
- _iterator23.e(err);
28222
+ _iterator22.e(err);
28224
28223
  } finally {
28225
- _iterator23.f();
28224
+ _iterator22.f();
28226
28225
  }
28227
28226
  }
28228
28227
  }, {
@@ -29012,10 +29011,10 @@
29012
29011
  }
29013
29012
  function setFrameSpec(partialResults) {
29014
29013
  var frameStack = [];
29015
- var _iterator24 = _createForOfIteratorHelper(partialResults), _step24;
29014
+ var _iterator23 = _createForOfIteratorHelper(partialResults), _step23;
29016
29015
  try {
29017
- for (_iterator24.s(); !(_step24 = _iterator24.n()).done; ) {
29018
- var partialResult = _step24.value;
29016
+ for (_iterator23.s(); !(_step23 = _iterator23.n()).done; ) {
29017
+ var partialResult = _step23.value;
29019
29018
  var frameSpec = frameStack.shift();
29020
29019
  if (!partialResult) {
29021
29020
  continue;
@@ -29025,9 +29024,9 @@
29025
29024
  frameStack.unshift.apply(frameStack, _toConsumableArray(frameSpecs));
29026
29025
  }
29027
29026
  } catch (err) {
29028
- _iterator24.e(err);
29027
+ _iterator23.e(err);
29029
29028
  } finally {
29030
- _iterator24.f();
29029
+ _iterator23.f();
29031
29030
  }
29032
29031
  }
29033
29032
  function getMergedFrameSpecs(_ref149) {
@@ -29632,8 +29631,8 @@
29632
29631
  help: 'Non-empty <td> elements in larger <table> must have an associated table header'
29633
29632
  },
29634
29633
  'td-headers-attr': {
29635
- description: 'Ensure that each cell in a table that uses the headers attribute refers only to other <th> elements in that table',
29636
- help: 'Table cell headers attributes must refer to other <th> elements in the same table'
29634
+ description: 'Ensure that each cell in a table that uses the headers attribute refers only to other cells in that table',
29635
+ help: 'Table cells that use the headers attribute must only refer to cells in the same table'
29637
29636
  },
29638
29637
  'th-has-data-cells': {
29639
29638
  description: 'Ensure that <th> elements and elements with role=columnheader/rowheader have data cells they describe',
@@ -30673,13 +30672,9 @@
30673
30672
  'td-headers-attr': {
30674
30673
  impact: 'serious',
30675
30674
  messages: {
30676
- pass: 'The headers attribute is exclusively used to refer to other header cells in the table',
30675
+ pass: 'The headers attribute is exclusively used to refer to other cells in the table',
30677
30676
  incomplete: 'The headers attribute is empty',
30678
- fail: {
30679
- 'cell-header-not-in-table': 'The headers attribute is not exclusively used to refer to other header cells in the table',
30680
- 'cell-header-not-th': 'The headers attribute must refer to header cells, not data cells',
30681
- 'header-refs-self': 'The element with headers attribute refers to itself'
30682
- }
30677
+ fail: 'The headers attribute is not exclusively used to refer to other cells in the table'
30683
30678
  }
30684
30679
  },
30685
30680
  'th-has-data-cells': {