@afixt/test-utils 1.1.7 → 1.2.0

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.
Files changed (70) hide show
  1. package/.claude/settings.local.json +6 -1
  2. package/.github/workflows/ci.yml +71 -0
  3. package/.github/workflows/pr-check.yml +88 -0
  4. package/.github/workflows/security.yml +139 -0
  5. package/.husky/pre-commit +1 -0
  6. package/.jscpd.json +27 -0
  7. package/.markdownlint.json +9 -0
  8. package/.prettierignore +13 -0
  9. package/.prettierrc +10 -0
  10. package/docs/arrayUtils.js.html +2 -2
  11. package/docs/domUtils.js.html +2 -2
  12. package/docs/getAccessibleName.js.html +2 -2
  13. package/docs/getAccessibleText.js.html +2 -2
  14. package/docs/getAriaAttributesByElement.js.html +2 -2
  15. package/docs/getCSSGeneratedContent.js.html +2 -2
  16. package/docs/getComputedRole.js.html +2 -2
  17. package/docs/getFocusableElements.js.html +2 -2
  18. package/docs/getGeneratedContent.js.html +2 -2
  19. package/docs/getImageText.js.html +2 -2
  20. package/docs/getStyleObject.js.html +2 -2
  21. package/docs/global.html +2 -2
  22. package/docs/hasAccessibleName.js.html +2 -2
  23. package/docs/hasAttribute.js.html +2 -2
  24. package/docs/hasCSSGeneratedContent.js.html +2 -2
  25. package/docs/hasHiddenParent.js.html +2 -2
  26. package/docs/hasParent.js.html +2 -2
  27. package/docs/hasValidAriaAttributes.js.html +2 -2
  28. package/docs/hasValidAriaRole.js.html +2 -2
  29. package/docs/index.html +2 -2
  30. package/docs/index.js.html +2 -2
  31. package/docs/isAriaAttributesValid.js.html +2 -2
  32. package/docs/isComplexTable.js.html +2 -2
  33. package/docs/isDataTable.js.html +2 -2
  34. package/docs/isFocusable.js.html +2 -2
  35. package/docs/isHidden.js.html +2 -2
  36. package/docs/isOffScreen.js.html +2 -2
  37. package/docs/isValidUrl.js.html +2 -2
  38. package/docs/isVisible.js.html +2 -2
  39. package/docs/module-afixt-test-utils.html +2 -2
  40. package/docs/scripts/core.js +726 -726
  41. package/docs/scripts/core.min.js +22 -22
  42. package/docs/scripts/resize.js +90 -90
  43. package/docs/scripts/search.js +265 -265
  44. package/docs/scripts/third-party/Apache-License-2.0.txt +202 -202
  45. package/docs/scripts/third-party/fuse.js +8 -8
  46. package/docs/scripts/third-party/hljs-line-num-original.js +369 -369
  47. package/docs/scripts/third-party/hljs-original.js +5171 -5171
  48. package/docs/scripts/third-party/popper.js +5 -5
  49. package/docs/scripts/third-party/tippy.js +1 -1
  50. package/docs/scripts/third-party/tocbot.js +671 -671
  51. package/docs/styles/clean-jsdoc-theme-base.css +1159 -1159
  52. package/docs/styles/clean-jsdoc-theme-dark.css +412 -412
  53. package/docs/styles/clean-jsdoc-theme-light.css +482 -482
  54. package/docs/styles/clean-jsdoc-theme-scrollbar.css +29 -29
  55. package/docs/testContrast.js.html +2 -2
  56. package/docs/testLang.js.html +2 -2
  57. package/docs/testOrder.js.html +2 -2
  58. package/eslint.config.mjs +84 -0
  59. package/package.json +68 -41
  60. package/scratchpads/issue-6-standardize-repo.md +109 -0
  61. package/src/getAccessibleName.js +156 -112
  62. package/src/getAccessibleText.js +71 -42
  63. package/src/stringUtils.js +19 -21
  64. package/src/testContrast.js +103 -22
  65. package/test/getAccessibleName.test.js +379 -315
  66. package/test/getAccessibleText.test.js +375 -308
  67. package/test/stringUtils.test.js +376 -332
  68. package/test/testContrast.test.js +801 -651
  69. package/.eslintrc +0 -78
  70. package/.github/workflows/test.yml +0 -26
@@ -1,5 +1,5 @@
1
- const { isEmpty } = require("./stringUtils.js");
2
- const { getAccessibleText } = require("./getAccessibleText.js");
1
+ const { isEmpty } = require('./stringUtils.js');
2
+ const { getAccessibleText } = require('./getAccessibleText.js');
3
3
 
4
4
  /**
5
5
  * Gets the accessible name of an element according to the accessible name calculation algorithm
@@ -18,8 +18,7 @@ function getAccessibleName(element) {
18
18
  // These are elements which are totally not able to be labeled at all.
19
19
  // Even if the title attribute is valid per HTML for these elements,
20
20
  // the title won't be used in any meaningful way by Accessibility APIs
21
- const unlabellable =
22
- "head *, hr, param, caption, colgroup, col, tbody, tfoot, thead, tr";
21
+ const unlabellable = 'head *, hr, param, caption, colgroup, col, tbody, tfoot, thead, tr';
23
22
 
24
23
  // STEP 0 - verify item is visible and can be labelled
25
24
  // if it isn't visible or can't be labelled then just bail
@@ -27,13 +26,15 @@ function getAccessibleName(element) {
27
26
  return false;
28
27
  }
29
28
 
30
- let id, ids, label;
29
+ let id;
30
+ let ids;
31
+ let label;
31
32
 
32
33
  // STEP 1 - always check for aria-labelledby first
33
34
  // STEP 1.1 - if aria-labelledby exists, check that the referenced element exists
34
35
  // STEP 1.1.1 - return the text from the referenced item
35
- if (element.hasAttribute("aria-labelledby")) {
36
- ids = element.getAttribute("aria-labelledby").trim().split(" ");
36
+ if (element.hasAttribute('aria-labelledby')) {
37
+ ids = element.getAttribute('aria-labelledby').trim().split(' ');
37
38
 
38
39
  const text = [];
39
40
  for (const id of ids) {
@@ -44,13 +45,13 @@ function getAccessibleName(element) {
44
45
  text.push(getAccessibleText(labelElement));
45
46
  }
46
47
 
47
- return text.join(" ");
48
+ return text.join(' ');
48
49
  }
49
50
 
50
51
  // STEP 2 - (next) always check for aria-label second
51
52
  // STEP 2.1 - if aria-label exists, return the text in it
52
- if (element.hasAttribute("aria-label")) {
53
- const ariaLabel = element.getAttribute("aria-label");
53
+ if (element.hasAttribute('aria-label')) {
54
+ const ariaLabel = element.getAttribute('aria-label');
54
55
  if (ariaLabel) {
55
56
  return ariaLabel;
56
57
  }
@@ -61,18 +62,35 @@ function getAccessibleName(element) {
61
62
  // Most ARIA controls/ widgets rely on labelling per steps 1 & 2 above.
62
63
  // If a label isn't found in Step 1 & 2, we can (for some roles) use DOM subtree content to find the label.
63
64
  // We check all of those here.
64
- if (element.hasAttribute("role")) {
65
- const roleValue = element.getAttribute("role");
65
+ if (element.hasAttribute('role')) {
66
+ const roleValue = element.getAttribute('role');
66
67
  const textRoles = [
67
- "button", "checkbox", "columnheader", "gridcell", "heading", "link",
68
- "listitem", "menuitem", "menuitemcheckbox", "menuitemradio", "option",
69
- "radio", "row", "rowgroup", "rowheader", "tab", "tooltip", "treeitem"
68
+ 'button',
69
+ 'checkbox',
70
+ 'columnheader',
71
+ 'gridcell',
72
+ 'heading',
73
+ 'link',
74
+ 'listitem',
75
+ 'menuitem',
76
+ 'menuitemcheckbox',
77
+ 'menuitemradio',
78
+ 'option',
79
+ 'radio',
80
+ 'row',
81
+ 'rowgroup',
82
+ 'rowheader',
83
+ 'tab',
84
+ 'tooltip',
85
+ 'treeitem',
70
86
  ];
71
87
 
72
88
  if (textRoles.includes(roleValue)) {
73
- // quick sanity check to make sure the object can hold text in the first place and that it actually has text in it
74
- if (!isEmpty(element.textContent)) {
75
- return getAccessibleText(element);
89
+ // Use getAccessibleText which handles both text nodes and
90
+ // image alt text in the subtree (not just textContent)
91
+ const text = getAccessibleText(element);
92
+ if (!isEmpty(text)) {
93
+ return text;
76
94
  }
77
95
  }
78
96
  }
@@ -85,7 +103,8 @@ function getAccessibleName(element) {
85
103
  // STEP 4.3 Otherwise use the title attribute
86
104
  // STEP 4.4 - return false. If none of the above yield a usable text string there is no accessible name
87
105
  if (
88
- matchesSelector(element,
106
+ matchesSelector(
107
+ element,
89
108
  'input:not([type]), input[type="text"], input[type="email"], input[type="password"], input[type="search"], input[type="tel"], input[type="url"], textarea'
90
109
  )
91
110
  ) {
@@ -102,14 +121,14 @@ function getAccessibleName(element) {
102
121
  }
103
122
 
104
123
  // if the element's parent is a label, use the text in that
105
- else if (element.closest("label")) {
106
- return getAccessibleText(element.closest("label"));
124
+ else if (element.closest('label')) {
125
+ return getAccessibleText(element.closest('label'));
107
126
  }
108
127
 
109
128
  // title attribute is last resort
110
- else if (element.hasAttribute("title")) {
111
- if (strlen(element.getAttribute("title")) > 0) {
112
- return element.getAttribute("title");
129
+ else if (element.hasAttribute('title')) {
130
+ if (strlen(element.getAttribute('title')) > 0) {
131
+ return element.getAttribute('title');
113
132
  } else {
114
133
  return false;
115
134
  }
@@ -129,17 +148,15 @@ function getAccessibleName(element) {
129
148
  // STEP 5.4 For input type=button: return title attribute
130
149
  // STEP 5.5 - return false. If none of the above yield a usable text string there is no accessible name
131
150
  if (
132
- matchesSelector(element,
133
- 'input[type="button"], input[type="submit"], input[type="reset"]'
134
- )
151
+ matchesSelector(element, 'input[type="button"], input[type="submit"], input[type="reset"]')
135
152
  ) {
136
- if (element.hasAttribute("value")) {
137
- if (element.getAttribute("value")) {
138
- return element.getAttribute("value");
153
+ if (element.hasAttribute('value')) {
154
+ if (element.getAttribute('value')) {
155
+ return element.getAttribute('value');
139
156
  }
140
157
  } else if (matchesSelector(element, 'input[type="button"]')) {
141
- if (element.hasAttribute("title")) {
142
- return element.getAttribute("title");
158
+ if (element.hasAttribute('title')) {
159
+ return element.getAttribute('title');
143
160
  }
144
161
  return false;
145
162
  }
@@ -149,16 +166,16 @@ function getAccessibleName(element) {
149
166
  // but we make a safe guess here anyway because getting the real name involves inspecting the Shadow DOM
150
167
  // which we cannot do
151
168
  else if (matchesSelector(element, 'input[type="submit"]')) {
152
- if (element.hasAttribute("title")) {
153
- return element.getAttribute("title");
169
+ if (element.hasAttribute('title')) {
170
+ return element.getAttribute('title');
154
171
  } else {
155
- return "Submit";
172
+ return 'Submit';
156
173
  }
157
174
  } else if (matchesSelector(element, 'input[type="reset"]')) {
158
- if (element.hasAttribute("title")) {
159
- return element.getAttribute("title");
175
+ if (element.hasAttribute('title')) {
176
+ return element.getAttribute('title');
160
177
  } else {
161
- return "Reset";
178
+ return 'Reset';
162
179
  }
163
180
  }
164
181
  }
@@ -170,12 +187,12 @@ function getAccessibleName(element) {
170
187
  // STEP 6.4: Otherwise use title attribute
171
188
  // STEP 6.5: return false. If none of the above yield a usable text string there is no accessible name
172
189
  if (matchesSelector(element, 'input[type="image"]')) {
173
- if (element.hasAttribute("alt")) {
174
- return element.getAttribute("alt");
175
- } else if (element.hasAttribute("value")) {
176
- return element.getAttribute("value");
177
- } else if (element.hasAttribute("title")) {
178
- return element.getAttribute("title");
190
+ if (element.hasAttribute('alt')) {
191
+ return element.getAttribute('alt');
192
+ } else if (element.hasAttribute('value')) {
193
+ return element.getAttribute('value');
194
+ } else if (element.hasAttribute('title')) {
195
+ return element.getAttribute('title');
179
196
  } else {
180
197
  return false;
181
198
  }
@@ -185,11 +202,11 @@ function getAccessibleName(element) {
185
202
  // STEP 7.1: use the button element subtree
186
203
  // STEP 7.2: use title attribute
187
204
  // STEP 7.3: return false. If none of the above yield a usable text string there is no accessible name
188
- if (element.tagName.toLowerCase() === "button") {
205
+ if (element.tagName.toLowerCase() === 'button') {
189
206
  if (strlen(getAccessibleText(element)) > 0) {
190
207
  return getAccessibleText(element);
191
- } else if (element.hasAttribute("title")) {
192
- return element.getAttribute("title");
208
+ } else if (element.hasAttribute('title')) {
209
+ return element.getAttribute('title');
193
210
  } else {
194
211
  return false;
195
212
  }
@@ -201,7 +218,8 @@ function getAccessibleName(element) {
201
218
  // STEP 8.3: return false. If none of the above yield a usable text
202
219
  // string there is no accessible name
203
220
  if (
204
- matchesSelector(element,
221
+ matchesSelector(
222
+ element,
205
223
  'select, input[type="checkbox"], input[type="color"], input[type="date"], input[type="datetime"], input[type="datetime-local"], input[type="email"], input[type="file"], input[type="month"], input[type="number"], input[type="radio"], input[type="range"], input[type="time"], input[type="week"]'
206
224
  )
207
225
  ) {
@@ -217,13 +235,13 @@ function getAccessibleName(element) {
217
235
  }
218
236
 
219
237
  // if the element's parent is a label, use the text in that
220
- else if (element.closest("label")) {
221
- return getAccessibleText(element.closest("label"));
238
+ else if (element.closest('label')) {
239
+ return getAccessibleText(element.closest('label'));
222
240
  }
223
241
 
224
242
  // title attribute is last resort
225
- else if (element.hasAttribute("title")) {
226
- return element.getAttribute("title");
243
+ else if (element.hasAttribute('title')) {
244
+ return element.getAttribute('title');
227
245
  }
228
246
 
229
247
  //if we got this far, there is no accessible name
@@ -242,20 +260,20 @@ function getAccessibleName(element) {
242
260
  // And these return values are especially not accurate for
243
261
  // non-english users, but we make a safe guess here anyway because
244
262
  // like Submit buttons, getting the real value involves inspecting Shadow DOM
245
- if (element.tagName.toLowerCase() === "details") {
246
- const summary = element.querySelector("summary");
263
+ if (element.tagName.toLowerCase() === 'details') {
264
+ const summary = element.querySelector('summary');
247
265
  if (summary) {
248
266
  if (strlen(getAccessibleText(summary)) > 0) {
249
267
  return getAccessibleText(summary);
250
268
  }
251
- } else if (element.hasAttribute("title")) {
252
- if (strlen(element.getAttribute("title")) > 0) {
253
- return element.getAttribute("title");
269
+ } else if (element.hasAttribute('title')) {
270
+ if (strlen(element.getAttribute('title')) > 0) {
271
+ return element.getAttribute('title');
254
272
  } else {
255
- return "Details";
273
+ return 'Details';
256
274
  }
257
275
  } else {
258
- return "Details";
276
+ return 'Details';
259
277
  }
260
278
  }
261
279
 
@@ -264,14 +282,14 @@ function getAccessibleName(element) {
264
282
  // STEP 10.2: title attribute
265
283
  // STEP 10.3: return false. If none of the above yield a usable text
266
284
  // string there is no accessible name
267
- if (element.tagName.toLowerCase() === "figure") {
268
- const figcaption = element.querySelector("figcaption");
285
+ if (element.tagName.toLowerCase() === 'figure') {
286
+ const figcaption = element.querySelector('figcaption');
269
287
  if (figcaption) {
270
288
  if (strlen(getAccessibleText(figcaption)) > 0) {
271
289
  return getAccessibleText(figcaption);
272
290
  }
273
- } else if (element.hasAttribute("title")) {
274
- return element.getAttribute("title");
291
+ } else if (element.hasAttribute('title')) {
292
+ return element.getAttribute('title');
275
293
  } else {
276
294
  return false;
277
295
  }
@@ -282,11 +300,11 @@ function getAccessibleName(element) {
282
300
  // STEP 11.2: use title attribute
283
301
  // STEP 11.3: return false. If none of the above yield a usable
284
302
  // text string there is no accessible name
285
- if (element.tagName.toLowerCase() === "img") {
286
- if (element.hasAttribute("alt")) {
287
- return element.getAttribute("alt");
288
- } else if (element.hasAttribute("title")) {
289
- return element.getAttribute("title");
303
+ if (element.tagName.toLowerCase() === 'img') {
304
+ if (element.hasAttribute('alt')) {
305
+ return element.getAttribute('alt');
306
+ } else if (element.hasAttribute('title')) {
307
+ return element.getAttribute('title');
290
308
  } else {
291
309
  return false;
292
310
  }
@@ -297,11 +315,11 @@ function getAccessibleName(element) {
297
315
  // STEP 11-1.2: use title attribute
298
316
  // STEP 11-1.3: return false. If none of the above yield a usable
299
317
  // text string there is no accessible name
300
- if (matchesSelector(element, "area[href]")) {
301
- if (element.hasAttribute("alt")) {
302
- return element.getAttribute("alt");
303
- } else if (element.hasAttribute("title")) {
304
- return element.getAttribute("title");
318
+ if (matchesSelector(element, 'area[href]')) {
319
+ if (element.hasAttribute('alt')) {
320
+ return element.getAttribute('alt');
321
+ } else if (element.hasAttribute('title')) {
322
+ return element.getAttribute('title');
305
323
  } else {
306
324
  return false;
307
325
  }
@@ -311,9 +329,9 @@ function getAccessibleName(element) {
311
329
  // STEP 11-2.1: use alt attribute
312
330
  // STEP 11-2.3: return false. If none of the above yield a usable
313
331
  // text string there is no accessible name
314
- if (element.tagName.toLowerCase() === "applet") {
315
- if (element.hasAttribute("alt")) {
316
- return element.getAttribute("alt");
332
+ if (element.tagName.toLowerCase() === 'applet') {
333
+ if (element.hasAttribute('alt')) {
334
+ return element.getAttribute('alt');
317
335
  } else {
318
336
  return false;
319
337
  }
@@ -325,9 +343,9 @@ function getAccessibleName(element) {
325
343
  // title attribute, or fallback content (handled at end via getAccessibleText)
326
344
  // STEP 11-3.1: use title attribute
327
345
  // STEP 11-3.2: fallback content is handled at the end via getAccessibleText
328
- if (element.tagName.toLowerCase() === "object") {
329
- if (element.hasAttribute("title")) {
330
- const titleValue = element.getAttribute("title");
346
+ if (element.tagName.toLowerCase() === 'object') {
347
+ if (element.hasAttribute('title')) {
348
+ const titleValue = element.getAttribute('title');
331
349
  if (strlen(titleValue) > 0) {
332
350
  return titleValue;
333
351
  }
@@ -343,16 +361,16 @@ function getAccessibleName(element) {
343
361
  // STEP 11-4.1: use title attribute
344
362
  // STEP 11-4.2: for native meter, use associated label
345
363
  // STEP 11-4.3: return false (don't use text content)
346
- if (element.tagName.toLowerCase() === "meter" || element.getAttribute("role") === "meter") {
347
- if (element.hasAttribute("title")) {
348
- const titleValue = element.getAttribute("title");
364
+ if (element.tagName.toLowerCase() === 'meter' || element.getAttribute('role') === 'meter') {
365
+ if (element.hasAttribute('title')) {
366
+ const titleValue = element.getAttribute('title');
349
367
  if (strlen(titleValue) > 0) {
350
368
  return titleValue;
351
369
  }
352
370
  }
353
371
 
354
372
  // For native meter elements, check for associated label
355
- if (element.tagName.toLowerCase() === "meter") {
373
+ if (element.tagName.toLowerCase() === 'meter') {
356
374
  // Check for label with for attribute
357
375
  if (element.id) {
358
376
  const label = document.querySelector('label[for="' + element.id + '"]');
@@ -362,11 +380,11 @@ function getAccessibleName(element) {
362
380
  }
363
381
 
364
382
  // Check for wrapping label
365
- const parentLabel = element.closest("label");
383
+ const parentLabel = element.closest('label');
366
384
  if (parentLabel) {
367
385
  // Get label text excluding the meter's own content
368
386
  const clone = parentLabel.cloneNode(true);
369
- const meterInClone = clone.querySelector("meter");
387
+ const meterInClone = clone.querySelector('meter');
370
388
  if (meterInClone) {
371
389
  meterInClone.remove();
372
390
  }
@@ -386,16 +404,16 @@ function getAccessibleName(element) {
386
404
  // STEP 12.3: use the summary attribute
387
405
  // STEP 12.4: return false. If none of the above yield a usable
388
406
  // text string there is no accessible name
389
- if (element.tagName.toLowerCase() === "table") {
390
- const caption = element.querySelector("caption");
407
+ if (element.tagName.toLowerCase() === 'table') {
408
+ const caption = element.querySelector('caption');
391
409
  if (caption) {
392
410
  if (strlen(getAccessibleText(caption)) > 0) {
393
411
  return getAccessibleText(caption);
394
412
  }
395
- } else if (element.hasAttribute("title")) {
396
- return element.getAttribute("title");
397
- } else if (element.hasAttribute("summary")) {
398
- return element.getAttribute("summary");
413
+ } else if (element.hasAttribute('title')) {
414
+ return element.getAttribute('title');
415
+ } else if (element.hasAttribute('summary')) {
416
+ return element.getAttribute('summary');
399
417
  }
400
418
  return false;
401
419
  }
@@ -405,11 +423,11 @@ function getAccessibleName(element) {
405
423
  // STEP 13.2: the title attribute
406
424
  // STEP 13.3: return false. If none of the above yield a usable
407
425
  // text string there is no accessible name
408
- if (matchesSelector(element, "a[href]")) {
426
+ if (matchesSelector(element, 'a[href]')) {
409
427
  if (strlen(getAccessibleText(element)) > 0) {
410
428
  return getAccessibleText(element);
411
- } else if (element.hasAttribute("title")) {
412
- return element.getAttribute("title");
429
+ } else if (element.hasAttribute('title')) {
430
+ return element.getAttribute('title');
413
431
  } else {
414
432
  return false;
415
433
  }
@@ -423,14 +441,15 @@ function getAccessibleName(element) {
423
441
  // STEP 14.2: return false. If none of the above yield a usable
424
442
  // text string there is no accessible name
425
443
  if (
426
- matchesSelector(element,
427
- "em, strong, small, s, cite, q, dfn, abbr, time, code, var, samp, kbd, sub, sup, i, b, u, mark, ruby, rt, rp, bdi, bdo, br, wbr"
444
+ matchesSelector(
445
+ element,
446
+ 'em, strong, small, s, cite, q, dfn, abbr, time, code, var, samp, kbd, sub, sup, i, b, u, mark, ruby, rt, rp, bdi, bdo, br, wbr'
428
447
  )
429
448
  ) {
430
449
  if (strlen(element.textContent) > 0) {
431
450
  return element.textContent;
432
- } else if (element.hasAttribute("title")) {
433
- return element.getAttribute("title");
451
+ } else if (element.hasAttribute('title')) {
452
+ return element.getAttribute('title');
434
453
  } else {
435
454
  return false;
436
455
  }
@@ -442,13 +461,13 @@ function getAccessibleName(element) {
442
461
  // via aria-labelledby, aria-label, or title attribute.
443
462
  // Since steps 1 & 2 already checked aria-labelledby and aria-label,
444
463
  // we only need to check title here, then return false.
445
- const isNavigation = element.tagName.toLowerCase() === "nav" ||
446
- element.getAttribute("role") === "navigation";
464
+ const isNavigation =
465
+ element.tagName.toLowerCase() === 'nav' || element.getAttribute('role') === 'navigation';
447
466
 
448
467
  if (isNavigation) {
449
468
  // Title attribute is valid for navigation landmarks
450
- if (element.hasAttribute("title")) {
451
- const titleValue = element.getAttribute("title");
469
+ if (element.hasAttribute('title')) {
470
+ const titleValue = element.getAttribute('title');
452
471
  if (strlen(titleValue) > 0) {
453
472
  return titleValue;
454
473
  }
@@ -476,14 +495,34 @@ function getAccessibleName(element) {
476
495
  */
477
496
  function isNotVisible(element) {
478
497
  // Importing isVisible would be better, but for this standalone function we'll check it this way
479
- if (!element) return true;
498
+ if (!element) {
499
+ return true;
500
+ }
480
501
 
481
502
  // These elements are inherently not visible
482
503
  // Note: 'area' is NOT included here because area elements DO have accessible names
483
504
  // via the alt attribute and should participate in accessible name calculation
484
505
  const nonVisibleSelectors = [
485
- 'base', 'head', 'meta', 'title', 'link', 'style', 'script', 'br', 'nobr', 'col', 'embed',
486
- 'input[type="hidden"]', 'keygen', 'source', 'track', 'wbr', 'datalist', 'param', 'noframes', 'ruby > rp'
506
+ 'base',
507
+ 'head',
508
+ 'meta',
509
+ 'title',
510
+ 'link',
511
+ 'style',
512
+ 'script',
513
+ 'br',
514
+ 'nobr',
515
+ 'col',
516
+ 'embed',
517
+ 'input[type="hidden"]',
518
+ 'keygen',
519
+ 'source',
520
+ 'track',
521
+ 'wbr',
522
+ 'datalist',
523
+ 'param',
524
+ 'noframes',
525
+ 'ruby > rp',
487
526
  ];
488
527
 
489
528
  if (nonVisibleSelectors.some(selector => matchesSelector(element, selector))) {
@@ -493,12 +532,16 @@ function isNotVisible(element) {
493
532
  // Check if display is none
494
533
  const isElemDisplayed = el => window.getComputedStyle(el).display === 'none';
495
534
 
496
- if (isElemDisplayed(element)) return true;
535
+ if (isElemDisplayed(element)) {
536
+ return true;
537
+ }
497
538
 
498
539
  // Check parent elements
499
540
  let parent = element.parentElement;
500
541
  while (parent) {
501
- if (isElemDisplayed(parent)) return true;
542
+ if (isElemDisplayed(parent)) {
543
+ return true;
544
+ }
502
545
  parent = parent.parentElement;
503
546
  }
504
547
 
@@ -512,12 +555,13 @@ function isNotVisible(element) {
512
555
  * @returns {boolean} True if element matches the selector
513
556
  */
514
557
  function matchesSelector(element, selector) {
515
- if (!element) return false;
558
+ if (!element) {
559
+ return false;
560
+ }
516
561
 
517
562
  // Use the right matches function depending on browser support
518
- const matchesMethod = element.matches ||
519
- element.mozMatchesSelector ||
520
- element.msMatchesSelector;
563
+ const matchesMethod =
564
+ element.matches || element.mozMatchesSelector || element.msMatchesSelector;
521
565
 
522
566
  // Handle multiple selectors (comma-separated)
523
567
  if (selector.includes(',')) {
@@ -533,7 +577,7 @@ function matchesSelector(element, selector) {
533
577
  * @returns {number} The string length or 0
534
578
  */
535
579
  function strlen(str) {
536
- return typeof str === "string" && !isEmpty(str.trim()) ? str.trim().length : 0;
580
+ return typeof str === 'string' && !isEmpty(str.trim()) ? str.trim().length : 0;
537
581
  }
538
582
 
539
583
  // Export the function for CommonJS module usage