testilo 51.2.1 → 52.1.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.
@@ -0,0 +1,13 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(gh pr *)",
5
+ "Read(//opt/homebrew/bin/**)",
6
+ "Bash(/opt/homebrew/bin/gh --version)",
7
+ "WebFetch(domain:github.com)",
8
+ "WebFetch(domain:patch-diff.githubusercontent.com)",
9
+ "Bash(curl -sL https://patch-diff.githubusercontent.com/raw/jrpool/testilo/pull/1.patch -o /tmp/pr1.patch)",
10
+ "Read(//tmp/**)"
11
+ ]
12
+ }
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testilo",
3
- "version": "51.2.1",
3
+ "version": "52.1.0",
4
4
  "description": "Prepares Testaro jobs and processes Testaro reports",
5
5
  "main": "call.js",
6
6
  "scripts": {
@@ -6444,7 +6444,7 @@ exports.issues = {
6444
6444
  weight: 1,
6445
6445
  tools: {
6446
6446
  ed11y: {
6447
- blockQuoteIsShort: {
6447
+ blockquoteIsShort: {
6448
6448
  variable: false,
6449
6449
  quality: 1,
6450
6450
  what: 'Block quote is shorter than 25 characters'
@@ -6895,31 +6895,36 @@ exports.issues = {
6895
6895
  'select-name': {
6896
6896
  variable: false,
6897
6897
  quality: 1,
6898
- what: 'select element has no accessible name'
6898
+ what: 'Element is select but has no accessible name'
6899
6899
  }
6900
6900
  },
6901
6901
  htmlcs: {
6902
6902
  'E-AAA.4_1_2.H91.Select.Name': {
6903
6903
  variable: false,
6904
6904
  quality: 1,
6905
- what: 'Select element has no accessible name'
6905
+ what: 'Element is select but has no accessible name'
6906
+ },
6907
+ 'E-WCAG2AAA.Principle4.Guideline4_1.4_1_2.H91.Select.Name': {
6908
+ variable: false,
6909
+ quality: 1,
6910
+ what: 'select element has no accessible name'
6906
6911
  },
6907
6912
  'E-AAA.4_1_2.H91.Select.Value': {
6908
6913
  variable: false,
6909
6914
  quality: 1,
6910
- what: 'Select element value has no accessible name'
6915
+ what: 'Element is select but its value has no accessible name'
6911
6916
  },
6912
6917
  'W-WCAG2AAA.Principle4.Guideline4_1.4_1_2.H91.Select.Value': {
6913
6918
  variable: false,
6914
6919
  quality: 1,
6915
- what: 'Select element value has no accessible name'
6920
+ what: 'Element is select but its value has no accessible name'
6916
6921
  }
6917
6922
  },
6918
6923
  wave: {
6919
6924
  select_missing_label: {
6920
6925
  variable: false,
6921
6926
  quality: 1,
6922
- what: 'Select element has no label'
6927
+ what: 'Element is select but has no label'
6923
6928
  }
6924
6929
  }
6925
6930
  }
@@ -6934,14 +6939,14 @@ exports.issues = {
6934
6939
  'Element option without attribute label must not be empty.': {
6935
6940
  variable: false,
6936
6941
  quality: 1,
6937
- what: 'option element is empty but has no label attribute'
6942
+ what: 'Element is option with no label attribute but is empty'
6938
6943
  }
6939
6944
  },
6940
6945
  nuVnu: {
6941
6946
  'Element option without attribute label must not be empty.': {
6942
6947
  variable: false,
6943
6948
  quality: 1,
6944
- what: 'option element is empty but has no label attribute'
6949
+ what: 'Element is option with no label attribute but is empty'
6945
6950
  }
6946
6951
  }
6947
6952
  }
@@ -8918,11 +8923,6 @@ exports.issues = {
8918
8923
  variable: false,
8919
8924
  quality: 1,
8920
8925
  what: 'ARIA hidden element is focusable or contains a focusable element'
8921
- },
8922
- 'presentation-role-conflict': {
8923
- variable: false,
8924
- quality: 1,
8925
- what: 'Element has a none/presentation role but is focusable or has a global ARIA state or property'
8926
8926
  }
8927
8927
  },
8928
8928
  ibm: {
@@ -9802,6 +9802,35 @@ exports.issues = {
9802
9802
  }
9803
9803
  }
9804
9804
  },
9805
+ presentationGlobal: {
9806
+ summary: 'global ARIA attribute nullifies presentation role',
9807
+ why: 'User encounters content intended to be hidden',
9808
+ wcag: '1.3.1',
9809
+ weight: 1,
9810
+ tools: {
9811
+ axe: {
9812
+ 'presentation-role-conflict': {
9813
+ variable: false,
9814
+ quality: 1,
9815
+ what: 'Element has a none/presentation role but is focusable or has a global ARIA state or property'
9816
+ }
9817
+ },
9818
+ nuVal: {
9819
+ 'The presentation role does not affect elements that have global ARIA attributes.': {
9820
+ variable: false,
9821
+ quality: 1,
9822
+ what: 'Element has a presentation role but also a global ARIA attribute that nullifies the role'
9823
+ }
9824
+ },
9825
+ nuVnu: {
9826
+ 'The presentation role does not affect elements that have global ARIA attributes.': {
9827
+ variable: false,
9828
+ quality: 1,
9829
+ what: 'Element has a presentation role but also a global ARIA attribute that nullifies the role'
9830
+ }
9831
+ }
9832
+ }
9833
+ },
9805
9834
  presentationTabIndexed: {
9806
9835
  summary: 'tabindex attribute nullifies presentation role',
9807
9836
  why: 'User encounters content intended to be hidden',
@@ -1,5 +1,6 @@
1
1
  /*
2
2
  © 2024 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jeff Witt. All rights reserved.
3
4
 
4
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
6
  of this software and associated documentation files (the "Software"), to deal
@@ -263,11 +264,12 @@ exports.scorer = report => {
263
264
  .texts
264
265
  .push(what);
265
266
  }
266
- issuePaths[issueName] ??= new Set();
267
+ issuePaths[issueName] ??= {};
267
268
  // If the element has a path ID:
268
269
  if (pathID) {
269
- // Ensure that it is in the issue-specific set of paths.
270
- issuePaths[issueName].add(pathID);
270
+ // Ensure that it is in the issue-specific map of paths to tools.
271
+ issuePaths[issueName][pathID] ??= new Set();
272
+ issuePaths[issueName][pathID].add(which);
271
273
  }
272
274
  }
273
275
  }
@@ -323,6 +325,10 @@ exports.scorer = report => {
323
325
  issueData.instanceCounts[toolName] = 0;
324
326
  }
325
327
  });
328
+ // Add the count of unique path-identified elements for the issue.
329
+ if (issuePaths[issueName]) {
330
+ issueData.uniqueElementCount = Object.keys(issuePaths[issueName]).length;
331
+ }
326
332
  });
327
333
  // Add the severity detail totals to the score.
328
334
  details.severity.total = Object
@@ -333,9 +339,22 @@ exports.scorer = report => {
333
339
  });
334
340
  return severityTotals;
335
341
  }, details.severity.total);
336
- // Add the element details to the score.
342
+ // Add the element details to the score, grouped by detecting tools.
337
343
  Object.keys(issuePaths).forEach(issueID => {
338
- details.element[issueID] = Array.from(issuePaths[issueID]);
344
+ details.element[issueID] = {};
345
+ const issueElementDetails = details.element[issueID];
346
+ // For each element reported as exhibiting the issue:
347
+ Object.keys(issuePaths[issueID]).forEach(pathID => {
348
+ // Convert the set of tools reporting it to a string.
349
+ const toolList = Array.from(issuePaths[issueID][pathID]).sort().join(' + ');
350
+ issueElementDetails[toolList] ??= [];
351
+ // Classify the path by the set of tools reporting its element for the issue.
352
+ issueElementDetails[toolList].push(pathID);
353
+ });
354
+ // Sort the paths within each tool list.
355
+ Object.keys(issueElementDetails).forEach(toolList => {
356
+ issueElementDetails[toolList].sort();
357
+ });
339
358
  });
340
359
  // Add the summary issue-count total to the score.
341
360
  summary.issueCount = Object.keys(details.issue).length * issueCountWeight;