diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7814376e4..17e4f314a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -75,6 +75,8 @@ jobs: run: echo ::set-env name=RELEASE_VERSION::${GITHUB_REF:10} - run: npm install working-directory: cypress-accessibility-checker + - run: npm run build:report + working-directory: cypress-accessibility-checker - run: npm run package:npm working-directory: cypress-accessibility-checker - run: gitactions/publish/cypress-achecker.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4b7cb405b..937d1ec33 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -207,7 +207,8 @@ jobs: working-directory: accessibility-checker-engine - run: npm install working-directory: cypress-accessibility-checker - + - run: npm run build:report + working-directory: cypress-accessibility-checker - run: npm test working-directory: cypress-accessibility-checker diff --git a/cypress-accessibility-checker/.achecker.yml b/cypress-accessibility-checker/.achecker.yml index 2ec3eb11c..71cf87930 100644 --- a/cypress-accessibility-checker/.achecker.yml +++ b/cypress-accessibility-checker/.achecker.yml @@ -50,6 +50,7 @@ captureScreenshots: false # Default: json outputFormat: - json + - html # Optional - Specify labels that you would like associated to your scan # diff --git a/cypress-accessibility-checker/README.md b/cypress-accessibility-checker/README.md index 3a53591d6..52242932d 100644 --- a/cypress-accessibility-checker/README.md +++ b/cypress-accessibility-checker/README.md @@ -1,6 +1,6 @@ # cypress-accessibility-checker -Cypress plugin for Accessibility Testing. This plugin is a wrapper around the NodeJS version of `accessibility-checker` which is [available on NPM](https://www.npmjs.com/package/accessibility-checker). The plugin works by taking a stringified version of the page and passing it down to a Cypress plugin which then executes the `accessibility-checker` library against the stringified page. Please see the `Usage` section in this README for more details. +Cypress plugin for Accessibility Testing. This plugin is a Cypress flavor of the NodeJS version of `accessibility-checker` which is also [available on NPM](https://www.npmjs.com/package/accessibility-checker). The plugin works by injecting the accessibility-checker engine into the Cypress browser and scanning the page in context. Please see the `Usage` section in this README for more details. ## Bugs and Issues @@ -12,7 +12,7 @@ All bugs or issues related to the cypress-accessibility-checker code can be crea ## Installation -Install the package as a devDependency. It will pull in a few packages including `accessibility-checker` which runs the actual tests. +Install the package as a devDependency. ``` npm install cypress-accessibility-checker --save-dev @@ -30,10 +30,10 @@ There are two setup steps you must complete in order for the Cypress tests to be In the `cypress/plugins/index.js` file located in your project, require the plugin and then register it with Cypress. ``` -const a11yTasks = require('cypress-accessibility-checker/plugin'); +const aCheckerTasks = require('cypress-accessibility-checker/plugin'); module.exports = (on, config) => { on('task', { - accessibilityChecker: a11yTasks + accessibilityChecker: aCheckerTasks }); }; ``` @@ -53,30 +53,26 @@ The typical use case will be to get the accessibility compliance of a document a ``` // Retrieves the compliance of the document then checks the results against the defined settings. // If there are issues when compared to the defined settings, it will fail the Cypress test. -cy.getA11yComplianceOfDocument('my scan').assertA11yCompliance() +cy.getCompliance('my scan').assertCompliance() ``` Examples on how to use each of the APIs below can be found in the `achecker.js` test file [located here](https://github.com/IBMa/equal-access/blob/master/cypress-accessibility-checker/test/cypress/integration/achecker.js). -- `cy.getA11yCompliance(content: string, label)` - - `content` must only be a string of HTML. Due to the nature of how the plugin works, only string version of HTML is supported. +- `cy.getCompliance(label)` + - Similar to `getCompliance()` in the reference API above. - Returned data ([defined here](https://www.npmjs.com/package/accessibility-checker#async-acheckergetcompliance-content--label--string)) will only contain the `report` object. -- `cy.getA11yComplianceOfDocument(label)` - - Similar to `getCompliance()` in the reference API above, however it will automatically pass in the document. +- `cy.getCompliance(cyObj, label)` + - Similar to `getCompliance()` in the reference API above, using the passed cy object (typically obtained via `cy.document`). - Returned data ([defined here](https://www.npmjs.com/package/accessibility-checker#async-acheckergetcompliance-content--label--string)) will only contain the `report` object. -- `cy.assertA11yCompliance(failOnError?: boolean)` +- `cy.assertCompliance(failOnError?: boolean)` - If `failOnError` is set to false, this will not fail your test. This is useful for testing what needs to be fixed without failing the test. By default this command will fail your test unless you specify `false` here. -- `cy.getA11yDiffResults(label)` -- `cy.getA11yBaseline(label)` -- `cy.diffA11yResultsWithExpected(actual, expected, clean)` -- `cy.stringifyA11yResults(report)` -- `cy.getA11yConfig()` -- `cy.closeA11y()` - -You can chain the commands similar to other Cypress commands. For example, `cy.getA11yComplianceOfDocument('my-label').assertA11yCompliance()` will get the compliance report of the document and then assert there are no violations or that it matches up with a baseline of the same label. - -**NOTE**: The results folder will contain scan results. Each file will contain the stringified version of what was scanned on the page instead of the URL scanned. This is currently working as expected. +- `cy.getDiffResults(label)` +- `cy.getBaseline(label)` +- `cy.diffResultsWithExpected(actual, expected, clean)` +- `cy.stringifyResults(report)` +- `cy.getACheckerConfig()` +You can chain the commands similar to other Cypress commands. For example, `cy.getCompliance('my-label').assertCompliance()` will get the compliance report of the document and then assert there are no violations or that it matches up with a baseline of the same label. ### Using Baselines Baselines are a helpful feature of `accessibility-checker` that can also be used in this Cypress wrapper. The concept involves capturing a scan result as a 'baseline' so that future scans will pass if they match the baseline. If they differ, then the test will fail. This feature is useful for things like false positives or issues you plan on not fixing. diff --git a/cypress-accessibility-checker/boilerplates/cypress/integration/accessibility-checker-example.spec.js b/cypress-accessibility-checker/boilerplates/cypress/integration/accessibility-checker-example.spec.js index 06c8e264f..6b21ffbdb 100644 --- a/cypress-accessibility-checker/boilerplates/cypress/integration/accessibility-checker-example.spec.js +++ b/cypress-accessibility-checker/boilerplates/cypress/integration/accessibility-checker-example.spec.js @@ -17,10 +17,10 @@ /// context('Accessibility checker example', () => { - it('Scan website that contains failures', () => { - // Replace URL with application URL - cy.visit('http://localhost:8080/example-html-file.html') - .getA11yComplianceOfDocument('example') // Label should be unique per call to the function - .assertA11yCompliance(); - }); + it('Scan website that contains failures', () => { + // Replace URL with application URL + cy.visit('http://localhost:8080/example-html-file.html') + .getCompliance('example') // Label should be unique per call to the function + .assertCompliance(); + }); }); diff --git a/cypress-accessibility-checker/cypress.json b/cypress-accessibility-checker/cypress.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/cypress-accessibility-checker/cypress.json @@ -0,0 +1 @@ +{} diff --git a/cypress-accessibility-checker/package.json b/cypress-accessibility-checker/package.json index c7ccec04e..56ca76b68 100644 --- a/cypress-accessibility-checker/package.json +++ b/cypress-accessibility-checker/package.json @@ -4,10 +4,11 @@ "description": "Accessibility Checker for Cypress", "scripts": { "lint": "eslint .", - "test": "start-server-and-test 'npm run test:start-http' 8080 'cd test && cypress run'", - "test:open": "cd test && cypress open", + "test": "npm run build:report && start-server-and-test 'npm run test:start-http' 8080 'cd test && cypress run'", + "test:open": "npm run build:report && cd test && cypress open", "test:start-http": "http-server -c-1 --silent", - "package:common": "rm -rf package && mkdir package && cp -R *.js src package/ && cp package.json package/ && cp README.md package/", + "build:report": "(cd ../report-react && npm run build && cd ../cypress-accessibility-checker && shx cp ../report-react/build/genReport.js ./src/lib/reporters/)", + "package:common": "npm run build:report && rm -rf package && mkdir package && cp -R *.js src package/ && cp package.json package/ && cp README.md package/", "package:zip": "npm run package:common && cd package && npm pack", "package:npm": "npm run package:common" }, @@ -32,7 +33,7 @@ "cypress": "^3 || ^4" }, "devDependencies": { - "cypress": "^4.5.0", + "cypress": "^5.2.0", "eslint": "^7.0.0", "eslint-plugin-cypress": "^2.10.3", "http-server": "^0.12.3", diff --git a/cypress-accessibility-checker/src/commands.js b/cypress-accessibility-checker/src/commands.js index 35a7aa4cf..b78477f91 100644 --- a/cypress-accessibility-checker/src/commands.js +++ b/cypress-accessibility-checker/src/commands.js @@ -16,133 +16,146 @@ /// -/** - * Scans the sent HTML and returns a report. - */ -Cypress.Commands.add('getA11yCompliance', (html, label) => { - return cy - .task('accessibilityChecker', { - task: 'getCompliance', - data: { - html, - label - } - }) - .then((result) => { - return cy.wrap(result, { log: false }); +const ACCommands = require("./lib/ACCommands"); +before(() => { + // To write to disk, we have to be outside of the browser, so that's a task + cy.task('accessibilityChecker', { + task: 'loadBaselines' + }).then((baselines) => { + return ACCommands.setBaselines(baselines); }); -}); +}) -/** - * Scans and returns a report using the entire `document` and the sent label. - */ -Cypress.Commands.add('getA11yComplianceOfDocument', (label) => { - return cy.document({ log: false }).then((doc) => { +after(() => { + // To write to disk, we have to be outside of the browser, so that's a task cy.task('accessibilityChecker', { - task: 'getCompliance', - data: { - html: doc.getElementsByTagName('html')[0].outerHTML, - label - } - }).then((result) => { - return cy.wrap(result, { log: false }); + task: 'onRunComplete' }); - }); +}) +// Note: Command run within the browser. Tasks execute outside of the browser + +/** + * Get compliance of a cypress object + * + * This can be called with a single parameter - getCompliance("SCAN_LABEL"), + * which will scan the current document. Otherwise, pass a cypress object + * (document) and a label + */ +Cypress.Commands.add("getCompliance", (cyObj, scanLabel) => { + let p; + if (typeof cyObj === "string") { + p = cy.document({ log: false }) + .then((doc) => { + scanLabel = cyObj; + cyObj = doc; + }) + } else { + // We already have a cypress object to scan + p = cy.wrap({}); + } + return p + // Allow the scan to run for 20 seconds + .then({ timeout: 20000 }, () => { + return ACCommands.getCompliance(cyObj, scanLabel); + }) + .then((result) => { + // To write to disk, we have to be outside of the browser, so that's a task + return cy.task('accessibilityChecker', { + task: 'sendResultsToReporter', + data: result + }).then(() => { + return result.report; + }); + }) }); /** - * Asserts a11y compliance against a baseline or failure level of violation. If a failure + * Asserts accessibility compliance against a baseline or failure level of violation. If a failure * is logged then the test will have an assertion fail. */ Cypress.Commands.add( - 'assertA11yCompliance', + 'assertCompliance', { prevSubject: true }, (priorResults, failOnError = true) => { - const taskResult = cy - .task('accessibilityChecker', { - task: 'assertCompliance', - data: { report: priorResults.report } - }) + const taskResult = ACCommands.assertCompliance(priorResults) .then((result) => { - const name = 'A11y'; - if (result === 0) { - // 0 - results match baseline or no violations based on failLevels - Cypress.log({ - name, - message: 'No violations based on baseline/failLevels' - }); return result; - } else if (result === 1) { - // 1 - results don't match baseline - // Get the diff between the results/baseline and put in console - cy.getA11yDiffResults(priorResults.report.label).then((diff) => { - const message = - 'Does not match baseline. See console for scan diff.'; - Cypress.log({ - name, - message, - consoleProps: () => { - return { - message, - diff - }; - } - }); - - return result; - }); - } else if (result === 2) { - // 2 - failure based on failLevels - // Print report and then individual violations - const message = - 'Violations according to failLevels. See console for scan results.'; - Cypress.log({ - name, - message, - consoleProps: () => { - return { - message, - priorResults - }; - } - }); - - // Individual violations - return cy.getA11yConfig().then(({ failLevels }) => { - priorResults.report.results - .filter((curErr) => failLevels.indexOf(curErr.level) !== -1) - .forEach((curErr) => { - Cypress.log({ - name, - message: curErr.message, - consoleProps: () => { - return { - curErr - }; - } - }); - }); - return result; - }); - } else if (result === -1) { - // -1 - Exception - Cypress.log({ - name, - message: 'Exception asserting compliance. See Cypress logs.' - }); - return result; - } + // const name = 'A11y'; + // if (result === 0) { + // // 0 - results match baseline or no violations based on failLevels + // Cypress.log({ + // name, + // message: 'No violations based on baseline/failLevels' + // }); + // return result; + // } else if (result === 1) { + // // 1 - results don't match baseline + // // Get the diff between the results/baseline and put in console + // cy.getDiffResults(priorResults.report.label).then((diff) => { + // const message = + // 'Does not match baseline. See console for scan diff.'; + // Cypress.log({ + // name, + // message, + // consoleProps: () => { + // return { + // message, + // diff + // }; + // } + // }); + + // return result; + // }); + // } else if (result === 2) { + // // 2 - failure based on failLevels + // // Print report and then individual violations + // const message = + // 'Violations according to failLevels. See console for scan results.'; + // Cypress.log({ + // name, + // message, + // consoleProps: () => { + // return { + // message, + // priorResults + // }; + // } + // }); + + // // Individual violations + // return cy.getACheckerConfig().then(({ failLevels }) => { + // priorResults.report.results + // .filter((curErr) => failLevels.indexOf(curErr.level) !== -1) + // .forEach((curErr) => { + // Cypress.log({ + // name, + // message: curErr.message, + // consoleProps: () => { + // return { + // curErr + // }; + // } + // }); + // }); + // return result; + // }); + // } else if (result === -1) { + // // -1 - Exception + // Cypress.log({ + // name, + // message: 'Exception asserting compliance. See Cypress logs.' + // }); + // return result; + // } }) - .then((result) => { - return cy.wrap(result, { log: false }); - }); - if (!!failOnError) { - const message = - 'accessibility-checker: See previous logs for accessibility violation data'; + // if (!!failOnError) { + // const message = + // 'accessibility-checker: See previous logs for accessibility violation data'; - taskResult.should('eq', 0, message); - } + // taskResult.should('eq', 0, message); + // } return taskResult; } @@ -151,19 +164,14 @@ Cypress.Commands.add( /** * Retrieves the diff of the results for the given label against the baseline. */ -Cypress.Commands.add('getA11yDiffResults', (label) => { - cy.task('accessibilityChecker', { - task: 'getDiffResults', - data: { label } - }).then((diff) => { - return cy.wrap(diff, { log: false }); - }); +Cypress.Commands.add('getDiffResults', (label) => { + return cy.wrap(ACCommands.getDiffResults(label), { log: false }); }); /** * Retrieves the baseline associated with the label. */ -Cypress.Commands.add('getA11yBaseline', (label) => { +Cypress.Commands.add('getBaseline', (label) => { cy.task('accessibilityChecker', { task: 'getBaseline', data: { label } @@ -176,7 +184,7 @@ Cypress.Commands.add('getA11yBaseline', (label) => { * Compare provided actual and expected objects and get the differences if there are any. */ Cypress.Commands.add( - 'diffA11yResultsWithExpected', + 'diffResultsWithExpected', (actual, expected, clean) => { cy.task('accessibilityChecker', { task: 'diffResultsWithExpected', @@ -191,7 +199,7 @@ Cypress.Commands.add( * Retrieve the readable stringified representation of the scan results. */ Cypress.Commands.add( - 'stringifyA11yResults', + 'stringifyResults', { prevSubject: true }, (report) => { // Send proper report property @@ -209,7 +217,7 @@ Cypress.Commands.add( /** * Retrieve the configuration object used by accessibility-checker. */ -Cypress.Commands.add('getA11yConfig', () => { +Cypress.Commands.add('getACheckerConfig', () => { cy.task('accessibilityChecker', { task: 'getConfig', data: {} @@ -217,15 +225,3 @@ Cypress.Commands.add('getA11yConfig', () => { return cy.wrap(result, { log: false }); }); }); - -/** - * Close puppeteer pages and other resources that may be used by accessibility-checker. - */ -Cypress.Commands.add('closeA11y', () => { - cy.task('accessibilityChecker', { - task: 'close', - data: {} - }).then((result) => { - return cy.wrap(result, { log: false }); - }); -}); diff --git a/cypress-accessibility-checker/src/engine/ace-node.js b/cypress-accessibility-checker/src/engine/ace-node.js new file mode 100644 index 000000000..27ca5f27e --- /dev/null +++ b/cypress-accessibility-checker/src/engine/ace-node.js @@ -0,0 +1,16 @@ +/*! + * Copyright:: 2016,2017,2019,2020- IBM, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +module.exports=function(e){var t={};function i(a){if(t[a])return t[a].exports;var l=t[a]={i:a,l:!1,exports:{}};return e[a].call(l.exports,l,l.exports,i),l.l=!0,l.exports}return i.m=e,i.c=t,i.d=function(e,t,a){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(i.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var l in e)i.d(a,l,function(t){return e[t]}.bind(null,l));return a},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i(i.s=8)}([function(e,t,i){"use strict";var a,l;Object.defineProperty(t,"__esModule",{value:!0}),t.RuleManual=t.RulePotential=t.RuleFail=t.RuleRender=t.RulePass=t.eRuleCategory=t.eRulePolicy=t.eRuleConfidence=void 0,function(e){e.PASS="PASS",e.FAIL="FAIL",e.POTENTIAL="POTENTIAL",e.MANUAL="MANUAL"}(a=t.eRuleConfidence||(t.eRuleConfidence={})),function(e){e.VIOLATION="VIOLATION",e.RECOMMENDATION="RECOMMENDATION",e.INFORMATION="INFORMATION"}(l=t.eRulePolicy||(t.eRulePolicy={})),function(e){e.ACCESSIBILITY="Accessibility",e.DESIGN="Design",e.OTHER="Other"}(t.eRuleCategory||(t.eRuleCategory={})),t.RulePass=function(e,t,i){if(null==e)throw new Error("Reason ID must be defined");return{value:[l.INFORMATION,a.PASS],reasonId:e,messageArgs:t||[],apiArgs:i||[]}},t.RuleRender=function(e,t,i){if(null==e)throw new Error("Reason ID must be defined");return{value:[l.INFORMATION,a.PASS],reasonId:0,messageArgs:t||[],apiArgs:i||[]}},t.RuleFail=function(e,t,i){if(null==e)throw new Error("Reason ID must be defined");return{value:[l.INFORMATION,a.FAIL],reasonId:e,messageArgs:t||[],apiArgs:i||[]}},t.RulePotential=function(e,t,i){if(null==e)throw new Error("Reason ID must be defined");return{value:[l.INFORMATION,a.POTENTIAL],reasonId:e,messageArgs:t||[],apiArgs:i||[]}},t.RuleManual=function(e,t,i){if(null==e)throw new Error("Reason ID must be defined");return{value:[l.INFORMATION,a.MANUAL],reasonId:e,messageArgs:t||[],apiArgs:i||[]}}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NodeWalker=t.ColorObj=t.RPTUtilStyle=t.RPTUtil=void 0;var a=i(2),l=i(6),o=function(){function e(){}return e.isDefinedAriaAttributeAtIndex=function(t,i){var a=t.attributes[i].name;return e.isDefinedAriaAttribute(t,a)},e.getAriaAttribute=function(t,i){var a=t.getAttribute(i);if(t.hasAttribute(i)&&""==a.trim())return a;if(!a){var o=t.nodeName.toLowerCase();i in e.ariaAttributeImplicitMappings&&(o in e.ariaAttributeImplicitMappings[i]?"function"==typeof(a=e.ariaAttributeImplicitMappings[i][o])&&(a=a(t)):"*"in e.ariaAttributeImplicitMappings[i]&&"function"==typeof(a=e.ariaAttributeImplicitMappings[i]["*"])&&(a=a(t)))}if(!a){var n=l.ARIAMapper.nodeToRole(t);n in e.ariaAttributeRoleDefaults&&i in e.ariaAttributeRoleDefaults[n]&&"function"==typeof(a=e.ariaAttributeRoleDefaults[n][i])&&(a=a(t))}return!a&&i in e.ariaAttributeGlobalDefaults&&(a=e.ariaAttributeGlobalDefaults[i]),a},e.wordCount=function(e){return 0==(e=e.trim()).length?0:e.split(/\s+/g).length},e.isTabbable=function(t){if(!e.isNodeVisible(t))return!1;if(t.hasAttribute("tabindex"))return parseInt(t.getAttribute("tabindex"))>=0;var i=t.nodeName.toLowerCase();if(i in e.tabTagMap){var a=e.tabTagMap[i];return"function"==typeof a&&(a=a(t)),a}return!1},e.tabIndexLEZero=function(t){if(e.hasAttribute(t,"tabindex")&&t.getAttribute("tabindex").match(/^-?\d+$/)){var i=parseInt(t.getAttribute("tabindex"));return 0==i||-1==i}return!1},e.isHtmlEquiv=function(e,t){var i=!1;if(e&&"input"==e.nodeName.toLowerCase()){var a=e.getAttribute("type").toLowerCase();a&&(-1!=t.indexOf("checkbox")?i="checkbox"==a:-1!=t.indexOf("radio")&&(i="radio"==a))}return i},e.isDefinedAriaAttribute=function(e,t){var i=!1;return"aria-"==t.substring(0,5)&&(i=e.hasAttribute&&e.hasAttribute(t)),i},e.normalizeSpacing=function(e){return e.trim().replace(/\s+/g," ")},e.nonExistantIDs=function(t,i){var a="";if(e.normalizeSpacing(i).length<1)return a;for(var l=i.split(" "),o=t.ownerDocument,n=0;n=2?a.substring(0,a.length-2):""},e.getDocElementsByTag=function(e,t){var i=e.ownerDocument;return t=t.toLowerCase(),i.RPT_DOCELEMSBYTAG||(i.RPT_DOCELEMSBYTAG={}),t in i.RPT_DOCELEMSBYTAG||(i.RPT_DOCELEMSBYTAG[t]=i.getElementsByTagName(t)),i.RPT_DOCELEMSBYTAG[t]},e.getChildByTag=function(t,i){return e.getChildByTagHidden(t,i,!1,!1)},e.getChildByTagHidden=function(t,i,a,l){for(var o=[],n=t.firstChild;null!=n;){if(n.nodeName.toLowerCase()==i){if((a||l&&!e.shouldCheckHiddenContent(n))&&!e.isNodeVisible(n)){n=n.nextSibling;continue}o.push(n)}n=n.nextSibling}return o},e.getElementsByRole=function(t,i){return e.getElementsByRoleHidden(t,i,!1,!1)},e.getElementsByRoleHidden=function(t,i,a,l){var o=null;if(null==(o=l?e.getCache(t,"RPTUtil_GETELEMENTSBY_ROLE_IMPLICIT",null):e.getCache(t,"RPTUtil_GETELEMENTSBY_ROLE",null))){o={};for(var n=t.body;null!=n.parentNode;)n=n.parentNode;for(var r=new s(n);r.nextNode();)if(!r.bEndTag){var u=[];if(r.node.hasAttribute&&r.node.hasAttribute("role")&&(u=r.node.getAttribute("role").split(" ")),0===u.length&&l){var d=e.getElementAriaProperty(r.node);d&&d.implicitRole&&(u=d.implicitRole)}if(0==u.length)continue;if(a&&e.shouldNodeBeSkippedHidden(r.node))continue;for(var c=0;c0&&e.concatUniqueArrayItemList(n,a)}return a},e.getImplicitRole=function(t){var i=e.getElementAriaProperty(t);return i&&i.implicitRole?i.implicitRole:[]},e.getRoleRequiredProperties=function(t,i){return null==t?null:a.ARIADefinitions.designPatterns[t]?"separator"===t.toLowerCase()?e.isFocusable(i)?a.ARIADefinitions.designPatterns[t].reqProps:null:a.ARIADefinitions.designPatterns[t].reqProps:null},e.isFocusable=function(t){return"undefined"!==t&&null!=t&&e.isTabbable(t)},e.hasRole=function(t,i,a){var l=!1;if(t&&t.hasAttribute&&t.hasAttribute("role"))if("string"!=typeof i)for(var o=t.getAttribute("role").trim().split(" "),n=0;!l&&n0&&(a=!0)}return a},e.isDataTable=function(t){return!(e.hasRole(t,"none")||e.hasRole(t,"presentation"))},e.isComplexDataTable=function(t){if("RPTUtil_isComplexDataTable"in t)return!!t.RPTUtil_isComplexDataTable;var i=!1;if(t&&e.isDataTable(t)){for(var a=null,l=null,o=t.getElementsByTagName("tr"),n=o.length,r=0,s=0,u=0,d=0;!i&&d1;for(var c=0;!i&&c2)&&e.getAncestor(l[m],"table")==t}else s>0&&++u,i=2==u;if(!i){var p=t.getElementsByTagName("thead"),_=p.length;_>0&&((i=_>1)||(i=p[0].getElementsByTagName("tr").length>1))}i||0===n||(i=s>0&&!e.isTableHeaderInFirstRowOrColumn(t))}return t.RPTUtil_isComplexDataTable=i,i},e.isTableHeaderInFirstRowOrColumn=function(t){var i=!1,a=t.rows;if(null!=a&&a.length>0){var l=a[0];if(!(i=l.cells.length>0&&0==e.getChildByTagHidden(l,"td",!1,!0).length)){i=!0;for(var o=0;i&&o=2?"."+t[1]:""},e.getFileAnchor=function(e){var t=e.match(/#(([^;?\.]|^$)+)([;?]|$)/);return null!=t&&t.length>=2?t[1]:""},e.checkObjEmbed=function(t,i,a){var l=t.nodeName.toLowerCase();if("object"!=l&&"embed"!=l&&"a"!=l&&"area"!=l)return!1;var o=!1;!o&&t.hasAttribute("type")&&(o=a(t.getAttribute("type").toLowerCase()));!o&&t.hasAttribute("codetype")&&(o=a(t.getAttribute("codetype")));if(!o){var n="";"embed"==l?n=t.getAttribute("src"):"a"==l||"area"==l?n=t.getAttribute("href"):t.hasAttribute("data")&&(n=t.getAttribute("data")),null==n&&(n=""),o=i(e.getFileExt(n))}if(!o&&"object"==l)for(var r=e.getChildByTagHidden(t,"param",!1,!0),s=0;!o&&null!=r&&s0)return e.getCache(t.ownerDocument,"RPTUtil_LABELS",{})[n]}return null},e.getElementAttribute=function(e,t){return e&&e.hasAttribute&&e.hasAttribute(t)?e.getAttribute(t):null},e.hasAriaLabel=function(t){return e.attributeNonEmpty(t,"aria-label")||e.attributeNonEmpty(t,"aria-labelledby")},e.hasImplicitLabel=function(t){var i=e.getAncestor(t,"label");if(i&&"label"===i.tagName.toLowerCase()&&e.isFirstFormElement(i,t)){var a=i.cloneNode(!0);return a=e.removeAllFormElementsFromLabel(a),e.hasInnerContentHidden(a)}return!1},e.isFirstFormElement=function(e,t){var i=["input","textarea","select","keygen","progress","meter","output"];if(null!=e.firstChild)for(var a=new s(e);a.nextNode();)if(-1!==i.indexOf(a.node.nodeName.toLowerCase()))return a.node===t;return!1},e.removeAllFormElementsFromLabel=function(e){for(var t=["input","textarea","select","button","datalist","optgroup","option","keygen","output","progress","meter"],i=e.childNodes,a=0;a-1&&e.removeChild(i[a]);return e},e.hasUniqueAriaLabelsLocally=function(t,i){if(0===t.length)return!1;var a=t[0].ownerDocument,l=!1,o=null;i&&(o=e.getCache(a,"RPTUtil_HAS_UNIQUE_ARIA_LABELS",null)),null==o&&(o={});for(var n=0;!l&&na){for(var o=0;ol){for(o=0;o0},e.getCache=function(e,t,i){var a=(e.nodeType,e);return null==a.aceCache&&(a.aceCache={}),null==a.aceCache[t]&&(a.aceCache[t]=i),a.aceCache[t]},e.setCache=function(e,t,i){var a=(e.nodeType,e);return null==a.aceCache&&(a.aceCache={}),a.aceCache[t]=i,i},e.getFrameByName=function(e,t){for(var i=[e.ownerDocument.defaultView],a=0;a0)},e.getInnerText=function(e){var t=e.innerText;return null!=t&&""!=t.trim()||(t=e.textContent),t},e.isInnerTextEmpty=function(t){var i=e.getInnerText(t);return!(null!=i&&i.trim().length>0)},e.hasInnerContent=function(t){var i=e.getInnerText(t),a=null!=i&&i.trim().length>0;if(null!=t.firstChild)for(var l=new s(t);!a&&l.nextNode();)a="img"==l.node.nodeName.toLowerCase()&&e.attributeNonEmpty(l.node,"alt");return a},e.hasInnerContentHidden=function(t){return e.hasInnerContentHiddenHyperLink(t,!1)},e.hasInnerContentHiddenHyperLink=function(t,i){if(!t)return!1;var a=!1;if(null!=t.firstElementChild)for(var l=new s(t);!a&&l.nextNode()&&l.node!=t;){var o=l.node;if(!(a="img"==o.nodeName.toLowerCase()&&e.attributeNonEmpty(o,"alt")&&e.isNodeVisible(o))&&1==o.nodeType&&e.isNodeVisible(o)&&!(a=!e.isInnerTextOnlyEmpty(o))&&1==i){a=e.attributeNonEmpty(o,"aria-label")||e.attributeNonEmpty(o,"aria-labelledby");var n=o.ownerDocument;if(n){var r=n.defaultView;if(r){var u=r.getComputedStyle(o);a||null==u||(a=(u.backgroundImage&&u.backgroundImage.indexOf||u.content)&&e.attributeNonEmpty(o,"alt"))}}}3==o.nodeType&&o.parentElement==t&&(a=!e.isInnerTextEmpty(o))}else a=!e.isInnerTextEmpty(t);return a},e.hasInnerContentOrAlt=function(t){var i=e.getInnerText(t),a=null!=i&&i.trim().length>0||e.attributeNonEmpty(t,"alt");if(null!=t.firstChild)for(var l=new s(t);!a&&l.nextNode()&&l.node!=t;)a="img"==l.node.nodeName.toLowerCase()&&e.attributeNonEmpty(l.node,"alt");return a},e.concatUniqueArrayItem=function(e,t){return-1===t.indexOf(e)&&null!==e&&t.push(e),t},e.concatUniqueArrayItemList=function(t,i){for(var a=0;null!==t&&a0?o["h1-6-with-aria-level-positive-integer"]:o["h1-6-without-aria-level-positive-integer"];break;case"header":var r=e.getAncestor(t,"article");null===r&&(r=e.getAncestor(t,"aside")),null===r&&(r=e.getAncestor(t,"main")),null===r&&(r=e.getAncestor(t,"nav")),null===r&&(r=e.getAncestor(t,"section")),l=null!==r?o["des-section-article"]:o["not-des-section-article"];break;case"hgroup":l=e.attributeNonEmpty(t,"aria-level")?o["with-aria-level"]:o["without-aria-level"];break;case"img":l=t.hasAttribute("alt")&&""===t.getAttribute("alt").trim()?o["img-with-empty-alt"]:o["img-without-empty-alt"];break;case"input":if(e.attributeNonEmpty(t,"type")){var s=t.getAttribute("type").trim().toLowerCase();if(null==(l=o[s]))switch(s){case"search":l=e.attributeNonEmpty(t,"list")?o["search-list"]:o["search-no-list"];break;case"text":l=e.attributeNonEmpty(t,"list")?o["text-with-list"]:o["text-no-list"];break;case"tel":l=e.attributeNonEmpty(t,"list")?o["tel-with-list"]:o["tel-no-list"];break;case"url":l=e.attributeNonEmpty(t,"list")?o["url-with-list"]:o["url-no-list"];break;case"email":l=e.attributeNonEmpty(t,"list")?o["email-with-list"]:o["email-no-list"];break;case"checkbox":l=e.attributeNonEmpty(t,"aria-pressed")?o["checkbox-with-aria-pressed"]:o["checkbox-without-aria-pressed"];break;default:l=e.attributeNonEmpty(t,"list")?o["text-with-list"]:o["text-no-list"]}}else l=e.attributeNonEmpty(t,"list")?o["text-with-list"]:o["text-no-list"];break;case"li":var u=t.parentNode;l=null===u||"ol"!==u.tagName.toLowerCase()&&"ul"!==u.tagName.toLowerCase()?o["parent-not-ol-or-ul"]:o["parent-ol-or-ul"];break;case"link":l=e.attributeNonEmpty(t,"href")?o["with-href"]:o["without-href"];break;case"menu":l=e.attributeNonEmpty(t,"type")&&"context"===t.getAttribute("type").trim().toLowerCase()?o["type-context"]:l;break;case"menuitem":e.attributeNonEmpty(t,"type")&&("command"===t.getAttribute("type").trim().toLowerCase()?l=o["type-command"]:"checkbox"===t.getAttribute("type").trim().toLowerCase()?l=o["type-checkbox"]:"radio"===t.getAttribute("type").trim().toLowerCase()&&(l=o["type-radio"])),null==l&&(l=o.default);break;case"option":var d=t.parentNode;l="datalist"===d.tagName.toLowerCase()||"options"===d.tagName.toLowerCase()?o["list-suggestion-datalist"]:o["not-list-suggestion-datalist"];break;case"select":o=a.ARIADefinitions.documentConformanceRequirementSpecialTags.select,l=t.hasAttribute("multiple")||e.attributeNonEmpty(t,"size")&&t.getAttribute("size")>1?o["multiple-attr-size-gt1"]:o["no-multiple-attr-size-gt1"];break;default:l=a.ARIADefinitions.textLevelSemanticElements.indexOf(i)>-1?a.ARIADefinitions.documentConformanceRequirementSpecialTags["text-level-semantic-elements"]:a.ARIADefinitions.documentConformanceRequirementSpecialTags.default}}return l||null},e.getAllowedAriaRoles=function(t,i){t.tagName.toLowerCase();var a=[],l=null;return null!=(l=null!=i&&void 0!==i?i:e.getElementAriaProperty(t))&&(null!==l.implicitRole&&e.concatUniqueArrayItemList(l.implicitRole,a),null!==l.validRoles&&e.concatUniqueArrayItemList(l.validRoles,a)),a},e.getAllowedAriaAttributes=function(t,i,l){var o=t.tagName.toLowerCase(),n=[];t.hasAttribute("disabled")&&-1===a.ARIADefinitions.elementsAllowedDisabled.indexOf(o)&&(n=e.concatUniqueArrayItem("aria-disabled",n)),t.hasAttribute("required")&&a.ARIADefinitions.elementsAllowedRequired.indexOf(o)>-1&&(n=e.concatUniqueArrayItem("aria-required",n)),t.hasAttribute("readonly")&&-1===a.ARIADefinitions.elementsAllowedReadOnly.indexOf(o)&&(n=e.concatUniqueArrayItem("aria-readonly",n)),t.hasAttribute("hidden")&&(n=e.concatUniqueArrayItem("aria-hidden",n));var r=null;r=null!=l&&void 0!==l?l:e.getElementAriaProperty(t);var s=!1;if("form"!==o&&"section"!==o||(s=!t.hasAttribute("aria-label")&&!t.hasAttribute("aria-labelledby")&&!t.hasAttribute("title")),null!=r){if(null!==r.implicitRole&&(null==i||0==i.length)&&!s)for(var u=0;u-1)return!0;if(null==e.unhideableElements||null==e.unhideableElements||-1==e.unhideableElements.indexOf(a)){if(!t.ownerDocument.defaultView)return!0;i=t.ownerDocument.defaultView.getComputedStyle(t,null);var l=t.getAttribute("hidden"),o=e.getCache(t,"PT_NODE_HIDDEN",void 0),n="boolean"==typeof t.hidden&&t.hidden;if(!(i||n||null!=l&&null!=l||o))return!0;if(null!==i&&("none"==i.getPropertyValue("display")||!t.Visibility_Check_Parent&&"hidden"==i.getPropertyValue("visibility"))||n||null!=l||o)return e.setCache(t,"PT_NODE_HIDDEN",!0),!1}var r=t.parentNode;if(null!=r&&1==r.nodeType){r.Visibility_Check_Parent=!0;var s=e.isNodeVisible(r);return s||e.setCache(t,"PT_NODE_HIDDEN",!0),s}return!0},e.getControlOfLabel=function(t){var i=e.getAncestor(t,"label");if(i&&i.hasAttribute("for"))return t.ownerDocument.getElementById(i.getAttribute("for"));for(var a={},l=t;l;){if(1===l.nodeType){var o=l;o.hasAttribute("id")&&(a[o.getAttribute("id")]=!0)}l=l.parentNode}for(var n=t.ownerDocument.querySelectorAll("*[aria-labelledby]"),r=0;r-1||r)return i=!0,e.setCache(t,"PT_NODE_DISABLED",i),!0;var s=t.parentNode;if(null!=s&&1==s.nodeType){var u=e.isNodeDisabled(s);return u&&(i=!0),e.setCache(t,"PT_NODE_DISABLED",i),u}return!1},e.shouldCheckHiddenContent=function(e){return!1},e.shouldNodeBeSkippedHidden=function(t){return!e.shouldCheckHiddenContent(t)&&!e.isNodeVisible(t)},e.isfocusableByDefault=function(t){return!("a"!=t.nodeName.toLowerCase()||!e.hasAttribute(t,"href"))||(!("area"!=t.nodeName.toLowerCase()||!e.hasAttribute(t,"href"))||-1!=["input","select","button","textarea","option","area"].indexOf(t.nodeName.toLowerCase()))},e.nonTabableChildCheck=function(t){if(!t.hasAttribute("tabindex")||-1!=parseInt(t.getAttribute("tabindex")))return!1;for(var i=new s(t);i.nextNode();){var a=i.node;if(1===a.nodeType&&(a.hasAttribute("tabindex")&&-1!=parseInt(a.getAttribute("tabindex"))&&!e.hasInnerContent(a)))return!1}return!0},e.Color=function(t){if("transparent"==(t=t.toLowerCase()))return new r(255,255,255,0);if(t in e.CSSColorLookup&&(t=e.CSSColorLookup[t]),t.startsWith("rgb(")){var i=/\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/;return null==(a=t.match(i))?null:new r(a[1],a[2],a[3])}if(t.startsWith("rgba(")){var a;i=/\s*rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(.+)\s*\)/;return null==(a=t.match(i))?null:new r(a[1],a[2],a[3],a[4])}if("#"!=t.charAt(0))return null;4==t.length&&(t="#"+t.charAt(1).repeat(2)+t.charAt(2).repeat(2)+t.charAt(3).repeat(2));var l=parseInt(t.substring(1,3),16),o=parseInt(t.substring(3,5),16),n=parseInt(t.substring(5,7),16);return new r(l,o,n)},e.ColorCombo=function(t){var i=t.ownerDocument;if(!i)return null;var a=i.defaultView;if(!a)return null;for(var l=[],o=t;o;)1==o.nodeType&&l.push(o),o=o.parentElement;var n={hasGradient:!1,hasBGImage:!1,fg:null,bg:null},r=a.getComputedStyle(t).color;r||(r="black");for(var s=e.Color(r),u=/transparent|rgba?\([^)]+\)/gi,d=function(e,t,i){try{if(void 0===e.length)return e;for(var a=null,l=null,o=1;o1e-4;){for(;u+s<=1&&r>i.contrastRatio(e[o].mix(e[o-1],u+s).getOverlayColor(t));)n=e[o].mix(e[o-1],u+s).getOverlayColor(t),r=i.contrastRatio(n),u+=s;for(;u-s>=0&&r>i.contrastRatio(e[o].mix(e[o-1],u-s).getOverlayColor(t));)n=e[o].mix(e[o-1],u-s).getOverlayColor(t),r=i.contrastRatio(n),u-=s;s/=10}(null==l||l>r)&&(l=r,a=n)}return a}catch(e){console.log(e)}return t},c=e.Color("white"),m=null,p=null,_=null;l.length>0;){var h=l.pop(),R=a.getComputedStyle(h);if(null!=R){var A=null;if(R.backgroundColor&&"transparent"!=R.backgroundColor&&"rgba(0, 0, 0, 0)"!=R.backgroundColor&&(A=e.Color(R.backgroundColor)),R.backgroundImage&&R.backgroundImage.indexOf&&-1!=R.backgroundImage.indexOf("gradient")){var g=R.backgroundImage.match(u);if(g){for(var f=[],b=0;b0&&parseFloat(R.opacity)<1?(null!=_&&(_.alpha=m*p,c=_.getOverlayColor(c)),m=1,p=null,_=null,R.opacity&&R.opacity.length>0&&(m=parseFloat(R.opacity)),null!=A&&(p=(_=A).alpha||1,delete _.alpha,1==m&&1==p&&(n.hasBGImage=!1,n.hasGradient=!1))):null!=A&&(null==_?(p=(_=A).alpha||1,delete _.alpha):_=A.getOverlayColor(_),1==m&&1==p&&1==(_.alpha||1)&&0==(A.alpha||1)&&(n.hasBGImage=!1,n.hasGradient=!1)),R.backgroundImage&&"none"!=R.backgroundImage&&(R.backgroundImage.indexOf&&-1!=R.backgroundImage.indexOf("gradient")?n.hasGradient=!0:n.hasBGImage=!0)}}return null!=_&&delete(s=s.getOverlayColor(_)).alpha,s.alpha=(s.alpha||1)*m,s=s.getOverlayColor(c),null!=_&&(_.alpha=m*p,c=_.getOverlayColor(c)),n.fg=s,n.bg=c,n},e.hasAttribute=function(e,t){var i=!1;if(e.hasAttribute)i=e.hasAttribute(t);else if(e.attributes&&e.attributes.getNamedItem){var a=e.attributes.getNamedItem(t);i=a&&a.specified}return i},e.unhideableElements=["area","param","audio"],e.hiddenByDefaultElements=["script","link","style","head","title","meta","base","noscript","template","datalist"],e.navLinkKeywords=["start","next","prev","previous","contents","index"],e.rulesThatHaveToCheckHidden=["RPT_Elem_UniqueId"],e.ariaAttributeRoleDefaults={alert:{"aria-live":"assertive","aria-atomic":"true"},checkbox:{"aria-checked":"false"},combobox:{"aria-expanded":"false","aria-haspopup":"listbox"},heading:{"aria-level":"2"},listbox:{"aria-orientation":"vertical"},log:{"aria-live":"polite"},menu:{"aria-orientation":"vertical"},menubar:{"aria-orientation":"horizontal"},menuitemcheckbox:{"aria-checked":"false"},menuitemradio:{"aria-checked":"false"},option:{"aria-selected":"false"},radio:{"aria-checked":"false"},scrollbar:{"aria-orientation":"vertical","aria-valuemin":"0","aria-valuemax":"100","aria-valuenow":function(t){var i=e.getAriaAttribute(t,"aria-valuemax"),a=e.getAriaAttribute(t,"aria-valuemin");return""+((i-a)/2+a)}},separator:{"aria-orientation":"horizontal","aria-valuemin":"0","aria-valuemax":"100","aria-valuenow":"50"},slider:{"aria-orientation":"horizontal","aria-valuemin":"0","aria-valuemax":"100","aria-valuenow":function(t){var i=e.getAriaAttribute(t,"aria-valuemax"),a=e.getAriaAttribute(t,"aria-valuemin");return""+((i-a)/2+a)}},spinbutton:{"aria-valuenow":"0"},status:{"aria-live":"polite","aria-atomic":"true"},switch:{"aria-checked":"false"},tab:{"aria-selected":"false"},tablist:{"aria-orientation":"horizontal"},toolbar:{"aria-orientation":"horizontal"},tree:{"aria-orientation":"vertical"}},e.ariaAttributeGlobalDefaults={"aria-atomic":"false","aria-autocomplete":"none","aria-busy":"false","aria-checked":void 0,"aria-current":"false","aria-disabled":"false","aria-dropeffect":"none","aria-expanded":void 0,"aria-grabbed":void 0,"aria-haspopup":"false","aria-hidden":void 0,"aria-invalid":"false","aria-live":"off","aria-modal":"false","aria-multiline":"false","aria-multiselectable":"false","aria-orientation":void 0,"aria-pressed":void 0,"aria-readonly":"false","aria-required":"false","aria-selected":void 0,"aria-sort":"none"},e.ariaAttributeImplicitMappings={"aria-autocomplete":{form:function(e){return"off"===e.getAttribute("autocomplete")?"none":"both"},input:function(e){return"off"===e.getAttribute("autocomplete")?"none":"both"},select:function(e){return"off"===e.getAttribute("autocomplete")?"none":"both"},textarea:function(e){return"off"===e.getAttribute("autocomplete")?"none":"both"}},"aria-checked":{input:function(e){return e.hasAttribute("indeterminate")?"mixed":""+e.hasAttribute("checked")},menuitem:function(e){return e.hasAttribute("indeterminate")?"mixed":""+e.hasAttribute("checked")},"*":function(e){if(e.hasAttribute("indeterminate"))return"mixed"}},"aria-haspopup":{"*":function(e){if(e.hasAttribute("contextmenu"))return"true"}},"aria-multiselectable":{input:function(e){if(e.hasAttribute("multiple"))return"true"}},"aria-expanded":{details:function(e){return e.getAttribute("open")},dialog:function(e){return e.getAttribute("open")}},"aria-placeholder":{input:function(e){return e.getAttribute("placeholder")},textarea:function(e){return e.getAttribute("placeholder")}},"aria-required":{input:function(e){return e.getAttribute("required")},select:function(e){return e.getAttribute("required")},textarea:function(e){return e.getAttribute("required")}},"aria-disabled":{button:function(e){return e.hasAttribute("disabled")?"true":"false"},fieldset:function(e){return e.hasAttribute("disabled")?"true":"false"},input:function(e){return e.hasAttribute("disabled")?"true":"false"},keygen:function(e){return e.hasAttribute("disabled")?"true":"false"},optgroup:function(e){return e.hasAttribute("disabled")?"true":"false"},option:function(e){return e.hasAttribute("disabled")?"true":"false"},select:function(e){return e.hasAttribute("disabled")?"true":"false"},textarea:function(e){return e.hasAttribute("disabled")?"true":"false"}}},e.tabTagMap={button:!0,input:function(e){return"hidden"!=e.getAttribute("type")},select:!0,textarea:!0,div:function(e){return e.hasAttribute("contenteditable")},a:function(e){return e.hasAttribute("href")},area:function(e){return e.hasAttribute("href")},audio:function(e){return e.hasAttribute("controls")},video:function(e){return e.hasAttribute("controls")}},e.CSSColorLookup={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32",buttontext:"rgba(0, 0, 0, 0.847)",buttonface:"#ffffff",graytext:"rgba(0, 0, 0, 0.247)"},e}();t.RPTUtil=o;var n=function(){function e(){}return e.getWeightNumber=function(e){var t={light:100,bold:700},i=parseInt(e);return i||(e in t?t[e]:400)},e.getFontInPixels=function(e){var t=parseFloat(e);if(!t)return{"xx-small":16,"x-small":10,small:13,medium:16,large:18,"x-large":24,"xx-large":32}[e];var i=e.substring((""+t).length);return""==i||"px"==i?t:"em"==i?16*t:"%"==i?t/100*16:"pt"==i?4*t/3:Math.round(t)},e}();t.RPTUtilStyle=n;var r=function(){function e(e,t,i,a){function l(e){if("string"!=typeof e)return e;var t=e;return"%"!=(t=t.trim())[t.length-1]?parseInt(t):Math.round(2.55*parseFloat(t.substring(0,t.length-1)))}this.red=l(e),this.green=l(t),this.blue=l(i),void 0!==a&&(this.alpha="string"==typeof a?parseFloat(a):a)}return e.prototype.toHexHelp=function(e){var t=Math.round(e).toString(16);return 1==t.length?"0"+t:t},e.prototype.toHex=function(){return"#"+this.toHexHelp(this.red)+this.toHexHelp(this.green)+this.toHexHelp(this.blue)},e.prototype.contrastRatio=function(e){var t=this;void 0!==this.alpha&&(t=this.getOverlayColor(e));var i=t.relativeLuminance();if(!e.relativeLuminance){var a="";for(var l in e)a+=l+"\n";alert(e),alert(a)}var o=e.relativeLuminance();return i>o?(i+.05)/(o+.05):(o+.05)/(i+.05)},e.prototype.relativeLuminance=function(){var e=this.red/255,t=this.green/255,i=this.blue/255;return.2126*(e=e<=.03928?e/12.92:Math.pow((e+.055)/1.055,2.4))+.7152*(t=t<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4))+.0722*(i=i<=.03928?i/12.92:Math.pow((i+.055)/1.055,2.4))},e.prototype.mix=function(t,i){if(void 0===this.alpha&&void 0===t.alpha)return new e(i*this.red+(1-i)*t.red,i*this.green+(1-i)*t.green,i*this.blue+(1-i)*t.blue);var a=this.alpha?this.alpha:1,l=t.alpha?t.alpha:1;return new e(i*this.red+(1-i)*t.red,i*this.green+(1-i)*t.green,i*this.blue+(1-i)*t.blue,i*a+(1-i)*l)},e.prototype.getOverlayColor=function(e){if(void 0===this.alpha||this.alpha>=1)return this;if(this.alpha<0)return null;if(void 0!==e.alpha&&e.alpha<1)return null;var t=this.mix(e,this.alpha);return delete t.alpha,t},e.fromCSSColor=function(t){var i=-1,a=-1,l=-1;if((t=t.toLowerCase()).startsWith("rgb(")){var n=/\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/;if(null==(r=t.match(n)))return null;i=r[1],a=r[2],l=r[3]}else if(t.startsWith("rgba(")){var r;n=/\s*rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(.+)\s*\)/;if(null==(r=t.match(n)))return null;i=r[1],a=r[2],l=r[3]}else{if("#"!=t.charAt(0)){if(!(t in o.CSSColorLookup))return null;t=o.CSSColorLookup[t]}var s=function(e){for(var t={a:10,b:11,c:12,d:13,e:14,f:15},i=0,a=0;a0&&!this.hierarchyChildrenHaveRole[this.hierarchyChildrenHaveRole.length-1]?(t="none",this.hierarchyChildrenHaveRole.push(!1)):(t=this.getRole(e)||"none",this.hierarchyChildrenHaveRole.push(this.childrenHaveRole(e,t))),this.hierarchyRole.push(t),"none"!==t){var i=this.hierarchyPath[this.hierarchyPath.length-1];i.roleCount[t]=(i.roleCount[t]||0)+1,this.hierarchyPath.push({rolePath:i.rolePath+"/"+t+"["+i.roleCount[t]+"]",roleCount:{}})}this.hierarchyResults.push({node:e,namespace:this.getNamespace(),role:t,attributes:this.getAttributes(e),rolePath:this.hierarchyPath[this.hierarchyPath.length-1].rolePath,bounds:this.getBounds(e)})},e.prototype.popHierarchy=function(){var e=this.hierarchyRole.pop();this.hierarchyChildrenHaveRole.pop(),"none"!==e&&this.hierarchyPath.pop(),this.hierarchyResults.pop()},e.prototype.openScope=function(e){return null===this.hierarchyRole&&this.reset(e),this.pushHierarchy(e),this.hierarchyResults},e.prototype.closeScope=function(e){for(var t=[],i=0,a=this.hierarchyResults;i0){for(var h="",R=0,A=d;R0&&(!a&&!l||!b))return n.getAttribute("aria-label").trim();if("presentation"!==f&&"none"!==f){if("img"===i.nodeName.toLowerCase()&&n.hasAttribute("alt"))return r.DOMUtil.cleanWhitespace(n.getAttribute("alt")).trim();if("input"===i.nodeName.toLowerCase()&&n.hasAttribute("id")&&n.getAttribute("id").length>0){var v=n.ownerDocument.querySelector("label[for='"+n.getAttribute("id")+"']");if(v)return this.computeNameHelp(e,v,!1,!1)}}if((l||a)&&b){if("textbox"===f)if("input"===n.nodeName.toLowerCase()){if(n.hasAttribute("value"))return n.getAttribute("value")}else l=!1;if("button"===f)if("input"===n.nodeName.toLowerCase()){var P=n.getAttribute("type").toLowerCase();if(["button","submit","reset"].includes(P)){if(n.hasAttribute("value"))return n.getAttribute("value");if("submit"===P)return"Submit";if("reset"===P)return"Reset"}}else l=!1;if("combobox"===f&&n.hasAttribute("aria-activedescendant")){var y=n.ownerDocument.getElementById("aria-activedescendant");if(y)return t.computeNameHelp(e,y,!1,!1)}if(["progressbar","scrollbar","slider","spinbutton"].includes(f)){if(n.hasAttribute("aria-valuetext"))return n.getAttribute("aria-valuetext");if(n.hasAttribute("aria-valuenow"))return n.getAttribute("aria-valuenow")}}if(!l&&(o.ARIADefinitions.nameFromContent(f)||a)){h="";var C=null;(C=n.ownerDocument.defaultView.getComputedStyle(n,"before").content)&&"none"!==C&&(h+=C=C.replace(/^"/,"").replace(/"$/,""));for(var T=new s.DOMWalker(n,!1,n);T.nextNode()&&!T.atRoot();)T.bEndTag||(h+=" "+t.computeNameHelp(e,T.node,a,!0));var I=null;try{I=n.ownerDocument.defaultView.getComputedStyle(n,"after").content}catch(e){}return I&&"none"!==I&&(h+=I=I.replace(/^"/,"").replace(/"$/,"")),h=h.replace(/\s+/g," ").trim()}return n.hasAttribute("title")?n.getAttribute("title"):""},t.nodeToRole=function(e){if(3===e.nodeType)return"text";if(1!==e.nodeType)return null;var i=e;if(!i||1!==i.nodeType)return null;if(i.hasAttribute("role")&&i.getAttribute("role").trim().length>0)return i.getAttribute("role").trim();var a=i.nodeName.toLowerCase();if(!(a in t.elemToRoleMap))return null;var l=t.elemToRoleMap[a];return"string"==typeof l?l:"function"==typeof l?l(i):null},t.hasParentRole=function(e,i){for(var a=e.parentNode;a;){if(t.nodeToRole(a)===i)return!0;a=a.parentNode}return!1},t.inputToRole=function(e){if(!e)return null;var i="text";if(e.hasAttribute("type")&&e.getAttribute("type").toLowerCase().trim().length>0&&(i=e.getAttribute("type").toLowerCase().trim()),!(i in t.inputToRoleMap))return null;var a=t.inputToRoleMap[i];return"string"==typeof a?a:"function"==typeof a?a(e):null},t.elemAttrValueCalculators={global:{name:t.computeName},datalist:{multiselectable:function(e){var t=e.getAttribute("id");if(t&&t.length>0){e.ownerDocument.querySelector("input[list='"+t+"']");return""+(e.getAttribute("multiple")&&("true"==e.getAttribute("multiple")||""==e.getAttribute("multiple")))}return null}},h1:{level:"1"},h2:{level:"2"},h3:{level:"3"},h4:{level:"4"},h5:{level:"5"},h6:{level:"6"},input:{checked:function(e){return"checkbox"===e.getAttribute("type")||"radio"===e.getAttribute("type")?""+e.checked:null},setsize:function(e){return null},posinset:function(e){return null},owns:function(e){return null}},keygen:{multiselectable:"false"},li:{setsize:function(e){var t=r.DOMUtil.getAncestor(e,["ol","ul","menu"]);if(!t)return null;var i=t.querySelectorAll("li"),a=t.querySelectorAll("ol li, ul li, menu li");return""+(i.length-a.length)},posinset:function(e){var t=r.DOMUtil.getAncestor(e,["ol","ul","menu"]);if(!t)return null;for(var i=t.querySelectorAll("li"),a=0,l=0;l1?"listbox":"combobox"},table:"table",textarea:"textbox",tbody:"rowgroup",td:function(e){for(var i=e.parentNode;i;){var a=t.nodeToRole(i);if("table"===a)return"cell";if("grid"===a)return"gridcell";i=i.parentNode}return null},th:function(e){if(e.hasAttribute("scope")){var i=e.getAttribute("scope").toLowerCase();if("row"===i)return"rowheader";if("col"===i)return"columnheader"}!e.hasAttribute("scope")||e.getAttribute("scope").toLowerCase();for(var a=e.parentNode;a;){var l=t.nodeToRole(a);if("table"===l)return"cell";if("grid"===l)return"gridcell";a=a.parentNode}return null},tfoot:"rowgroup",thead:"rowgroup",tr:"row",ul:"list"}),t}(n.CommonMapper);t.ARIAMapper=d},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.AncestorUtil=void 0;var a=function(){function e(){}return e.isPresentationFrame=function(e){if(e&&e.dom)for(var t=e.dom.length-2;t>=0;--t){var i=e.dom[t].node;if(1===i.nodeType&&"presentation"===i.getAttribute("role")||"true"===i.getAttribute("aria-hidden"))return!0}return!1},e}();t.AncestorUtil=a},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Config=t.checkDemo=t.ARIAMapper=t.Context=t.Checker=void 0;var a=i(9);Object.defineProperty(t,"Context",{enumerable:!0,get:function(){return a.Context}});var l=i(11);Object.defineProperty(t,"Checker",{enumerable:!0,get:function(){return l.Checker}});var o=i(6);Object.defineProperty(t,"ARIAMapper",{enumerable:!0,get:function(){return o.ARIAMapper}});var n=i(4);Object.defineProperty(t,"Config",{enumerable:!0,get:function(){return n.Config}}),String.prototype.startsWith=String.prototype.startsWith||function(e){return 0===this.indexOf(e)},String.prototype.includes=String.prototype.includes||function(e){return-1!==this.indexOf(e)},Array.prototype.includes=Array.prototype.includes||function(e){return-1!==this.indexOf(e)},t.checkDemo=function(e){e||(e=0);var t=new l.Checker;setTimeout((function(){t.check(document.documentElement,["IBM_Accessibility","IBM_Design"]).then((function(e){console.log(e);for(var t={FAIL:0,POTENTIAL:1,MANUAL:2,PASS:3},i=0;i0)throw new Error("Cannot have !attr"+i+" context");if(e&&void 0!==i&&i.length>0&&(void 0===a||0===a.length))throw new Error("Cannot have equivalence check without a value")}return e.prototype.matches=function(e){var t=e.attributes;if(this.attr in t){if(this.inclusive){if(this.eq){var i=t[this.attr];if("="===this.eq)return this.value===i;if("!="===this.eq)return this.value!==i;if("~"===this.eq)return this.value===i;if("!~"===this.eq)return this.value!==i;throw new Error("Context equivalence operator not supported")}return!0}return!1}return!this.inclusive},e}();t.AttrInfo=a;var l=function(){function e(e,t,i,a,l){if(this.inclusive=e,this.namespace=t,this.role=i,this.attrs=a,this.connector=l,"*"===i&&!e)throw new Error("!* context not supported")}return e.prototype.matches=function(e,t){var i=this.namespace in e&&(e[this.namespace][t].role===this.role||"none"!==e[this.namespace][t].role&&"*"===this.role);if(!i||"*"!==this.role||"dom"!==this.namespace||"#text"!==e[this.namespace][t].role&&"/#text"!==e[this.namespace][t].role||(i=!1),this.inclusive&&!i)return!1;if(!this.inclusive&&!i)return!0;for(var a=this.attrs,l=e[this.namespace][t],o=!0,n=0,r=a;n+~]?/g);i+~]?)/),r=[],s=0,u=n[3].match(/\[([^\]]+)\]/g)||[];s+~,])/g,"$1")).replace(/([>+~,]) +/g,"$1")).replace(/ +/g," ")).trim()},e.parse=function(t){for(var i=e.splitMultiple(e.cleanContext(t)),a=[],l=0;l=0;a--)"data-namewalk"!==i[a].name&&(t+=" "+i[a].name+'="'+i[a].value+'"');return t+=">"},e.prototype.run=function(t,i,a,l){var o,n=(new Date).getTime();try{o=this.rule.run(i,a,l)}catch(e){var r=e;throw console.error("RULE EXCEPTION:",this.rule.id,i.dom.rolePath,r.stack),e}var s=(new Date).getTime();o||(o=[]),o instanceof Array||(o=[o]);for(var u=[],d=0,c=o;d0&&t.ownerDocument.getElementById(m)}if(s.DOMUtil.isNodeVisible(o.node)||"style"===o.node.nodeName.toLowerCase()||"datalist"===o.node.nodeName.toLowerCase()||"param"===o.node.nodeName.toLowerCase()||!s.DOMUtil.getAncestor(o.node,["body"])){var p={};for(var _ in d){var h=d[_],R=h[h.length-1];p[_]=R}for(var A={},g=0,f=this.getMatchingRules(d);g=1&&(s=a+"$$"+i),++i;var d=new u(e,r);this.wrappedRuleMap[s]=d;var c=d.parsedInfo.contextInfo,m=c[c.length-1],p=m.namespace+":"+m.role;m.inclusive?(this.inclRules[p]=this.inclRules[p]||[],this.inclRules[p].push(d)):(this.exclRules[p]=this.exclRules[p]||[],this.exclRules[p].push(d))}}},e.prototype.addNlsMap=function(e){for(var t in e)this.nlsMap[t]=e[t]},e.prototype.addHelpMap=function(e){for(var t in e)this.helpMap[t]=e[t]},e.prototype.getMessage=function(e,t,i){var a=e.indexOf("$$");if(a>=0&&(e=e.substring(0,a)),!(e in this.nlsMap))return e;var l=this.nlsMap[e][t||0];return l?l.replace(/\{(\d+)\}/g,(function(e,t,a){return i[t]})):e+"_"+t},e.prototype.getHelp=function(e,t){var i=e.indexOf("$$");if(i>=0&&(e=e.substring(0,i)),!(e in this.helpMap))return e;return((t=t||0)in this.helpMap[e]?this.helpMap[e][t||0]:this.helpMap[e][0])||e+"_"+t},e.prototype.addMapper=function(e){this.mappers[e.getNamespace()]=e},e.match=function(e,t){var i=e.length-1,a=t.dom.length-1;if(!e[i].matches(t,a))return!1;for(--i,--a;a>=0&&i>=0;){var l=e[i],o=e[i].matches(t,a);if(">"===l.connector){if(!o)return!1;--i,--a}else{if(" "!==l.connector)throw new Error("Context connector "+l.connector+" is not supported");if(l.inclusive)o&&--i,--a;else{if(!o)return!1;for(var n=!1,r=a-1;!n&&r>=0;--r)n=!e[i].matches(t,r);if(n)return!1;--i}}}return-1===i},e.prototype.getMatchingRules=function(t){var i={},a=[];function l(l){for(var o=0,n=l;o0;return i?a.RulePass(1):i?void 0:a.RuleManual("Manual_1")}}];t.a11yRulesCanvas=l},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesFrame=void 0;var a=i(0),l=i(1),o=[{id:"WCAG20_Frame_HasTitle",context:"dom:frame, dom:iframe",run:function(e,t){var i=e.dom.node;return l.RPTUtil.hasRole(i,"presentation")?null:l.RPTUtil.attributeNonEmpty(i,"title")?a.RulePass("Pass_0"):a.RuleFail("Fail_1")}},{id:"Valerie_Frame_SrcHtml",context:"dom:frame, dom:iframe",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.attributeNonEmpty(i,"src")&&l.RPTUtil.isHtmlExt(l.RPTUtil.getFileExt(i.getAttribute("src")));return o?a.RulePass("Pass_0"):o?void 0:a.RulePotential("Potential_1")}}];t.a11yRulesFrame=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesMeta=void 0;var a=i(0),l=[{id:"WCAG20_Meta_RedirectZero",context:"dom:meta[http-equiv][content]",run:function(e,t){var i=e.dom.node;if("refresh"==i.getAttribute("http-equiv").toLowerCase()){var l=i.getAttribute("content").toLowerCase();return-1!=l.indexOf("url")&&!l.startsWith("0;")?a.RuleFail("Fail_1"):a.RulePass("Pass_0")}return null}},{id:"RPT_Meta_Refresh",context:"dom:meta[http-equiv][content]",run:function(e,t){var i=e.dom.node;return"refresh"!=i.getAttribute("http-equiv").toLowerCase()?a.RulePass("Pass_0"):-1==i.getAttribute("content").toLowerCase().indexOf("url=")?a.RulePotential("Potential_1"):a.RulePass("Pass_0")}}];t.a11yRulesMeta=l},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesColor=void 0;var a=i(0),l=i(1),o=[{id:"IBMA_Color_Contrast_WCAG2AA",context:"dom:*",run:function(e,t){var i=e.dom.node,o=i.nodeName.toLowerCase();if(!l.RPTUtil.isNodeVisible(i)||null!=l.RPTUtil.hiddenByDefaultElements&&null!=l.RPTUtil.hiddenByDefaultElements&&l.RPTUtil.hiddenByDefaultElements.indexOf(o)>-1)return null;for(var n="",r=i.childNodes,s=0;s=24||A>=18.6&&R>=700,f=h>=4.5||h>=3&&g,b=m.hasBGImage||m.hasGradient,v=l.RPTUtil.isNodeDisabled(i);if(!v){var P=l.RPTUtil.getControlOfLabel(i);P&&(v=l.RPTUtil.isNodeDisabled(P))}return l.RPTUtil.setCache(i,"EXT_Color_Contrast_WCAG2AA",{ratio:h,isLargeScale:g,weight:R,size:A,hasBackground:b,isDisabled:v}),b?null:(!f&&v&&(f=!0),f?a.RulePass("Pass_0",[h.toFixed(2),A,R,p.toHex(),_.toHex(),m.hasBGImage,m.hasGradient]):a.RuleFail("Fail_1",[h.toFixed(2),A,R,p.toHex(),_.toHex(),m.hasBGImage,m.hasGradient]))}},{id:"IBMA_Color_Contrast_WCAG2AA_PV",context:"dom:*",dependencies:["IBMA_Color_Contrast_WCAG2AA"],run:function(e,t){var i=e.dom.node,o=i.nodeName.toLowerCase();if(l.RPTUtil.isNodeDisabled(i)||!l.RPTUtil.isNodeVisible(i)||null!=l.RPTUtil.hiddenByDefaultElements&&null!=l.RPTUtil.hiddenByDefaultElements&&l.RPTUtil.hiddenByDefaultElements.indexOf(o)>-1)return null;var n=l.RPTUtil.getCache(i,"EXT_Color_Contrast_WCAG2AA",null);if(!n)return a.RulePass("Pass_0");var r=n.ratio>=4.5||n.ratio>=3&&n.isLargeScale;return!r&&n.isDisabled&&(r=!0),r?a.RulePass("Pass_0",[n.ratio.toFixed(2),n.size,n.weight]):a.RulePotential("Potential_1",[n.ratio.toFixed(2),n.size,n.weight])}},{id:"IBMA_Link_Contrast_WCAG2AA",context:"a[href] | *[onclick]",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.ColorCombo(i),n=l.RPTUtil.getCache(i,"EXT_Link_Contrast_WCAG2AA",null);if(null===n){n={};for(var r="",s=i.childNodes,u=0;u=24||R>=18.6&&h>=700;for(var A=function(e){var t=c.getComputedStyle(e),i=l.RPTUtilStyle.getWeightNumber(t.fontWeight),a=l.RPTUtilStyle.getFontInPixels(t.fontSize),n=l.RPTUtil.ColorCombo(e),r=o.fg.contrastRatio(n.fg),s=o.bg.contrastRatio(n.bg),u=Math.abs(i-h)>=300||Math.abs(a-R)>5||t.textDecoration!=_.textDecoration;return{ratio:Math.max(r,s),fgRatio:r,bgRatio:s,scaleChange:u,colorCombo:n}},g=new l.NodeWalker(i);g.prevNode()&&!m(g.node)&&!p(g.node);)if(3==g.node.nodeType&&g.node.nodeValue.trim().length>0){g.node=g.node.parentNode,n.prev=A(g.node);break}for(var f=new l.NodeWalker(i,!0);f.nextNode()&&!m(f.node)&&!p(f.node);)if(3==f.node.nodeType&&f.node.textContent.trim().length>0){f.node=f.node.parentNode,n.next=A(f.node);break}}l.RPTUtil.setCache(i,"EXT_Link_Contrast_WCAG2AA",n);var b=!0,v=0,P=null,y=n.isLargeScale?3:4.5;return n.prev?(b=n.prev.ratio>=y||n.prev.scaleChange)||(v=n.prev.fgRatio,P=n.prev.colorCombo):n.next&&((b=b&&n.next.ratio>=y||n.next.scaleChange)||(v=n.next.fgRatio,P=n.next.colorCombo)),b?a.RulePass("Pass_0"):a.RulePotential("Potential_1",[v.toFixed(2),o.fg.toHex(),P.fg.toHex()])}}];t.a11yRulesColor=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesHeading=void 0;var a=i(0),l=i(1),o=[{id:"RPT_Header_HasContent",context:"dom:h1, dom:h2, dom:h3, dom:h4, dom:h5, dom:h6",run:function(e,t){var i=e.dom.node;return l.RPTUtil.hasInnerContentHidden(i)?a.RulePass("Pass_0"):a.RuleFail("Fail_1")}},{id:"RPT_Header_Trigger",context:"dom:h1, dom:h2, dom:h3, dom:h4, dom:h5, dom:h6",dependencies:["RPT_Header_HasContent"],run:function(e,t){var i=e.dom.node,o=l.RPTUtil.triggerOnce(i.ownerDocument,"RPT_Header_Trigger",!1);return o?a.RulePass("Pass_0"):o?void 0:a.RulePotential("Potential_1")}},{id:"RPT_Headers_FewWords",context:"dom:h1, dom:h2, dom:h3, dom:h4, dom:h5, dom:h6",dependencies:["RPT_Header_HasContent"],run:function(e,t){var i=e.dom.node,o=l.RPTUtil.wordCount(l.RPTUtil.getInnerText(i))<=20;return o?a.RulePass("Pass_0"):o?void 0:a.RulePotential("Potential_1")}},{id:"RPT_Block_ShouldBeHeading",context:"dom:p, dom:div, dom:br",run:function(e,t){for(var i=e.dom.node,o=0,n=new l.NodeWalker(i),r=!1;!r&&n.nextNode()&&n.node!=i&&n.node!=i.parentNode&&!["br","div","p"].includes(n.node.nodeName.toLowerCase());){var s=n.node.nodeName.toLowerCase();if("b"!=s&&"em"!=s&&"i"!=s&&"strong"!=s&&"u"!=s&&"font"!=s||l.RPTUtil.shouldNodeBeSkippedHidden(n.node))r=1==n.node.nodeType&&l.RPTUtil.attributeNonEmpty(n.node,"alt")&&("applet"==s||"embed"==s||"img"==s||"input"==s&&n.node.hasAttribute("type")&&"image"==n.node.getAttribute("type"))||"#text"==s&&n.node.nodeValue.trim().length>0||"a"==s&&n.node.hasAttribute("href")&&l.RPTUtil.attributeNonEmpty(n.node,"href");else{var u=l.RPTUtil.wordCount(l.RPTUtil.getInnerText(n.node));u>0,r=(o+=u)>10,n.bEndTag=!0}}return 0==o&&(r=!0),r?a.RulePass("Pass_0"):r?void 0:a.RulePotential("Potential_1")}}];t.a11yRulesHeading=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesMobile=void 0;var a=i(0),l=i(1),o=[{id:"HAAC_Aria_ImgAlt",context:"dom:*[role]",run:function(e,t){var i=e.dom.node;if(!i.getAttribute("role").includes("img")||!l.RPTUtil.hasRole(i,"img"))return null;if("true"===i.getAttribute("aria-hidden"))return null;var o=l.RPTUtil.getAriaLabel(i).length>0;return o||(o=l.RPTUtil.attributeNonEmpty(i,"title")),o?a.RulePass("Pass_0"):a.RuleFail("Fail_2")}}];t.a11yRulesMobile=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesApplet=void 0;var a=i(0),l=i(1),o=[{id:"WCAG20_Applet_HasAlt",context:"dom:applet",run:function(e,t){var i=e.dom.node;if(l.RPTUtil.attributeNonEmpty(i,"alt")){var o=i.getAttribute("alt").trim();return i.hasAttribute("code")&&o==i.getAttribute("code").trim()?a.RuleFail("Fail_2"):l.RPTUtil.hasInnerContentHidden(i)?a.RulePass("Pass_0"):a.RuleFail("Fail_3")}return a.RuleFail("Fail_1")}}];t.a11yRulesApplet=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesCombobox=void 0;var a=i(0),l=i(1),o=[{id:"HAAC_Combobox_Must_Have_Text_Input",context:"aria:combobox",run:function(e,t){var i=e.dom.node;if(!l.RPTUtil.isNodeVisible(i)||l.RPTUtil.isNodeDisabled(i))return null;var o=i.tagName.toLowerCase(),n="input"===i.nodeName.toLowerCase()?"1.0":"1.1",r="true"===l.RPTUtil.getAriaAttribute(i,"aria-expanded").trim().toLowerCase(),s=!0,u=null;if("1.0"===n)s="false"===l.RPTUtil.getAriaAttribute(i,"aria-multiline").trim().toLowerCase(),u=i;else{s=!1;for(var d=new l.NodeWalker(i);!s&&d.nextNode()&&d.node!=i&&d.node!=i.nextSibling;)1===d.node.nodeType&&l.RPTUtil.isNodeVisible(d.node)&&!l.RPTUtil.isNodeDisabled(d.node)&&(s=(l.RPTUtil.hasRoleInSemantics(d.node,"textbox")||l.RPTUtil.hasRoleInSemantics(d.node,"searchbox"))&&"false"===l.RPTUtil.getAriaAttribute(d.node,"aria-multiline").trim().toLowerCase(),u=d.node);if(!s){var c=l.RPTUtil.getElementAttribute(i,"aria-owns");if(c)for(var m=l.RPTUtil.normalizeSpacing(c.trim()).split(" "),p=0;!s&&p0||null!=r&&r.length>0)o=!0;else for(var s=l.RPTUtil.getDocElementsByTag(i,"a"),u={},d=0;!o&&d0;if(!l)for(var o=i.nextSibling;!l&&null!=o;){if("noembed"==o.nodeName.toLowerCase())l=!0;else{if("#text"==o.nodeName.toLowerCase()&&o.nodeValue.trim().length>0)break;if(1==o.nodeType)break}o=o.nextSibling}return l?a.RulePass("Pass_0"):l?void 0:a.RulePotential("Potential_1")}},{id:"Valerie_Noembed_HasContent",context:"dom:noembed",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.hasInnerContentHidden(i);return o?a.RulePass("Pass_0"):o?void 0:a.RulePotential("Potential_1")}},{id:"RPT_Embed_HasAlt",context:"dom:embed",run:function(e,t){var i=e.dom.node;return l.RPTUtil.attributeNonEmpty(i,"alt")?a.RulePass("Pass_0"):a.RulePotential("Potential_1")}},{id:"RPT_Embed_AutoStart",context:"dom:param[name=autoplay], dom:param[name=autostart], dom:embed[flashvars], dom:embed[src], dom:*[autostart=true], dom:*[autostart=1], dom:bgsound",run:function(e,t){var i,l=e.dom.node,o=l.nodeName.toLowerCase();if("bgsound"==o)i=!1;else if("param"==o){var n="";l.hasAttribute("value")&&(n=l.getAttribute("value").toLowerCase()),i=0==n.indexOf("0;")||!(-1!=n.indexOf("true")||-1!=n.indexOf("1"))}else if("embed"==o){var r;if(i=!0,l.hasAttribute("flashvars"))i=-1==(r=l.getAttribute("flashvars")).indexOf("autostart=true")&&-1==r.indexOf("autostart=1");if(i&&l.hasAttribute("src"))i=-1==(r=l.getAttribute("src")).indexOf("autostart=true")&&-1==r.indexOf("autostart=1")}if(i&&l.hasAttribute("autostart")){var s=l.getAttribute("autostart").toLowerCase();i="true"!=s&&"1"!=s}return i?a.RulePass("Pass_0"):a.RulePotential("Potential_1")}}];t.a11yRulesEmbed=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesImg=void 0;var a=i(0),l=i(1),o=[{id:"WCAG20_Img_HasAlt",context:"dom:img",run:function(e,t){var i=e.dom.node,l=i.hasAttribute("alt");if(l){var o=i.getAttribute("alt");if(0==o.trim().length&&0!=o.length)return a.RuleFail("Fail_1")}return l?a.RulePass("Pass_0"):a.RuleFail("Fail_2")}},{id:"WCAG20_Img_PresentationImgHasNonNullAlt",context:"dom:img[alt]",run:function(e,t){var i=e.dom.node,o=!0;return(l.RPTUtil.hasRole(i,"presentation")||l.RPTUtil.hasRole(i,"none"))&&(o=0==i.getAttribute("alt").length),o?a.RulePass("Pass_0"):a.RuleFail("Fail_1")}},{id:"WCAG20_Img_LinkTextNotRedundant",context:"dom:img[alt]",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.getAncestor(i,"a");if(null==o)return null;var n=i.getAttribute("alt").trim().toLowerCase();if(0==n.length)return null;var r=o.innerText,s="";if(null!=r&&(s=r.trim().toLowerCase()),!(s.length>0)){for(var u=!0,d=new l.NodeWalker(o);u&&d.prevNode();){if("#text"==(m=(c=d.node).nodeName.toLowerCase())&&c.nodeValue.length>0||"img"==m&&l.RPTUtil.attributeNonEmpty(c,"alt"))break;"a"!=m||l.RPTUtil.shouldNodeBeSkippedHidden(c)||(u=(c.innerText||c.textContent||"").trim().toLowerCase()!=n)}if(!u)return a.RuleFail("Fail_2");for(d=new l.NodeWalker(o,!0);u&&d.nextNode();){var c,m;if("#text"==(m=(c=d.node).nodeName.toLowerCase())&&c.nodeValue.length>0||"img"==m&&l.RPTUtil.attributeNonEmpty(c,"alt"))break;"a"!=m||l.RPTUtil.shouldNodeBeSkippedHidden(c)||(u=c.innerText.trim().toLowerCase()!=n)}return u?a.RulePass("Pass_0"):a.RuleFail("Fail_3")}return n==s?a.RuleFail("Fail_1"):void 0}},{id:"WCAG20_Img_AltTriggerNonDecorative",context:"dom:img[alt]",run:function(e,t){var i=e.dom.node;if(l.RPTUtil.hasRole(i,"presentation")||l.RPTUtil.hasRole(i,"none")||0==i.getAttribute("alt").length)return a.RulePass(1);var o={bulletMax:{value:30,type:"integer"},horizMinWidth:{value:400,type:"integer"},horizMaxHeight:{value:30,type:"integer"}},n=-1,r=-1;return i.hasAttribute("height")&&(n=parseInt(i.getAttribute("height"))),i.hasAttribute("width")&&(r=parseInt(i.getAttribute("width"))),-1!=n&&-1!=r&&(r<=o.bulletMax.value&&n<=o.bulletMax.value||r>=o.horizMinWidth.value&&n<=o.horizMaxHeight.value)?a.RulePass("Pass_0"):a.RulePotential("Potential_1")}},{id:"WCAG20_Img_TitleEmptyWhenAltNull",context:"dom:img[alt]",run:function(e,t){var i=e.dom.node;return i.getAttribute("alt").trim().length>0?null:l.RPTUtil.attributeNonEmpty(i,"title")?a.RuleFail("Fail_1"):a.RulePass("Pass_0")}},{id:"RPT_Img_UsemapValid",context:"dom:img[ismap]",run:function(e,t){var i=e.dom.node,o=!1;if(i.hasAttribute("usemap")){var n=i.getAttribute("usemap"),r=(n=n.trim().toLowerCase()).indexOf("#");if(-1!=r&&(n=n.substr(r+1)),n.length>0)for(var s=l.RPTUtil.getDocElementsByTag(i,"map"),u=0;!o&&u0){for(var r=["short description"],s=0;o&&s0||l.RPTUtil.getChildByTagHidden(i,"option",!1,!0).length<=10;return o?a.RulePass("Pass_0"):o?void 0:a.RulePotential("Potential_1")}}];t.a11yRulesSelect=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesAria=void 0;var a=i(0),l=i(1),o=i(2),n=[{id:"Rpt_Aria_ValidRole",context:"dom:*[role]",run:function(e,t){for(var i=e.dom.node,l=o.ARIADefinitions.designPatterns,n=[],r=0,s=i.getAttribute("role").trim().toLowerCase().split(/\s+/);r0?a.RuleFail("Fail_1",[n.join(",")]):a.RulePass("Pass_0")}},{id:"Rpt_Aria_ValidProperty",context:"dom:*",run:function(e,t){var i=e.dom.node,n=i.attributes;if(n){for(var r=o.ARIADefinitions.propertyDataTypes,s="",u=0,d=0,c=n.length;d1&&A.includes("all"))n.push(d[m].nodeValue.split(" ")),r.push(_),s.push(h.values.toString());else for(var g={},f=0;f=1&&g.length>1){n=!1;for(var f=0,b=g.length;f=1)for(var v=0,P=g.length;v0){var y=s.getElementById(g[v]);if((n=null!=y)&&!h.hiddenIDRefSupported&&(n=l.RPTUtil.isNodeVisible(y)),n)break;""!=g[v]&&d.push(g[v])}}}n||r.push(_)}}var C=0==r.length,T=new Array,I=new Array,O=new Array;return C||(I.push(r.join(", ")),O.push(i.nodeName.toLowerCase()),d.length>0&&T.push(d.join(", "))),0==c?null:C?a.RulePass("Pass_0"):a.RuleFail("Fail_1",[T.toString(),I.toString(),O.toString()])}},{id:"Rpt_Aria_RequiredChildren_Native_Host_Sematics",context:"dom:*[role]",dependencies:["Rpt_Aria_ValidRole"],run:function(e,t){var i=e.dom.node,n=!1,r=o.ARIADefinitions.designPatterns,s=i.getAttribute("role").trim().toLowerCase().split(/\s+/),u=(i.ownerDocument,new Array),d=new Array;i.nodeName.toLowerCase();if(l.RPTUtil.isNodeDisabled(i))return a.RulePass("Pass_0");for(var c=0,m=s.length;c-1)return a.RulePass("Pass_0");if(!l.RPTUtil.hasInnerContentHidden(n)&&-1===["area","input","embed","button","textarea","select"].indexOf(r))return a.RulePass("Pass_0");if("table"==r&&l.RPTUtil.isLayoutTable(n))return null;var s=!0,u=l.RPTUtil.hasRole(n,i.mapPossibleOrphanedWidgets,!0);"link"===r&&(u=!1);var d=r in i.mapPossibleOrphanedElements;if(!u&&!d)return null;if(!(s=l.RPTUtil.getAncestorWithRole(n,i.mapLandmarks,!0))){for(var c=n.parentElement;!s&&null!=c;)s=l.RPTUtil.getCache(c,"Rpt_Aria_OrphanedContent",!1),c=c.parentElement;for(c=n.nextElementSibling;!s&&null!=c;)s=l.RPTUtil.getCache(c,"Rpt_Aria_OrphanedContent",!1),c=c.nextElementSibling;for(c=n.previousElementSibling;!s&&null!=c;)s=l.RPTUtil.getCache(c,"Rpt_Aria_OrphanedContent",!1),c=c.previousElementSibling;if(s)return null;if(l.RPTUtil.setCache(n,"Rpt_Aria_OrphanedContent",!0),s=l.RPTUtil.getCache(n,"Rpt_Aria_OrphanedContent_NoTrigger",!1)||l.RPTUtil.hasRole(n,i.mapNoLandmarkedRoles,!0)||l.RPTUtil.getAncestorWithRole(n,i.mapNoLandmarkedRoles,!0))return l.RPTUtil.setCache(n,"Rpt_Aria_OrphanedContent_NoTrigger",!0),null}return s?a.RulePass("Pass_0"):a.RuleFail("Fail_1")}},{id:"Rpt_Aria_InvalidTabindexForActivedescendant",context:"dom:*[aria-activedescendant]",run:function(e,t){var i,o=e.dom.node,n=o.nodeName.toLowerCase();if(o.hasAttribute("class")&&"mbl"==o.getAttribute("class").substring(0,3))return null;if(l.RPTUtil.isNodeDisabled(o))return null;i=l.RPTUtil.tabIndexLEZero(o);var r=new Array;r.push(n);var s=new Array;return s.push(o.getAttribute("aria-activedescendant").split(" ").join(", ")),i?a.RulePass("Pass_0"):a.RuleFail("Fail_1",[r.toString(),s.toString()])}},{id:"Rpt_Aria_EventHandlerMissingRole_Native_Host_Sematics",context:"dom:*[onclick],dom:*[onblur], dom:*[ondblclick], dom:*[onfocus], dom:*[onkeydown],dom:*[onkeypress], dom:*[onkeyup], dom:*[onmousedown], dom:*[onmouseup], dom:*[onmousemove], dom:*[onmouseout], dom:*[onmouseover], dom:*[onresize], dom:*[onchange]",run:function(e,t){var i=e.dom.node;if(l.RPTUtil.getAncestor(i,"svg"))return null;if(l.RPTUtil.hasAnyRole(i,!0))return a.RulePass("Pass_0");if(l.RPTUtil.isfocusableByDefault(i))return a.RulePass("Pass_0");var o=new Array;o.push(i.nodeName.toLowerCase());for(var n=new Array,r=["onblur","onfocus","onchange","onclick","ondblclick","onkeydown","onkeypress","onkeyup","onmousedown","onmouseup","onmousemove","onmouseout","onmouseover","onresize"],s=0;s0&&u.length>0?(v.push(d),P.push(u.join(", "))):n=!0,c?n?a.RulePass("Pass_0"):a.RuleFail("Fail_1",[v.toString(),P.toString()]):null}},{id:"Rpt_Aria_MissingKeyboardHandler",context:"dom:*[role]",dependencies:["Rpt_Aria_ValidRole"],run:function(e,t){for(var i=e.dom.node,n=!0,r=n,s=i.ownerDocument,u=o.ARIADefinitions.designPatterns,d=i.getAttribute("role").trim().toLowerCase().split(/\s+/),c=l.RPTUtil.hasAttribute,m=["combobox","grid","listbox","menu","menubar","radiogroup","tablist","tree","treegrid"],p=new Array,_=0;_=0)if(!(c(i,"aria-disabled")?i.getAttribute("aria-disabled"):"")&&!(n=i.hasAttribute("onkeydown")||i.hasAttribute("onkeypress")))if(c(i,"aria-activedescendant"))n=!0;else{var h=o.ARIADefinitions.designPatterns[d[_]].reqChildren;if(h)for(var R=0,A=h.length;R11;P=y.iterateNext()}if(!n)break;b=f.iterateNext()}else{var y;for(v="descendant::*",P=(y=s.evaluate(v,i,l.RPTUtil.defaultNSResolver,0,null)).iterateNext();P&&!n;)if(l.RPTUtil.shouldNodeBeSkippedHidden(P))P=y.iterateNext();else{var C;if(!(n=l.RPTUtil.tabIndexLEZero(P)&&(P.hasAttribute("onkeydown")||P.hasAttribute("onkeypress"))))if(l.RPTUtil.isfocusableByDefault(P))if(n=P.hasAttribute("onkeydown")||P.hasAttribute("onkeypress"),"a"==P.nodeName.toLowerCase()&&P.hasAttribute("href"))n=(C=P.getAttribute("href")).startsWith("javascript:")&&-1==C.indexOf("void")&&C.length>11;P=y.iterateNext()}}}n||p.push(d[_]),!n&&r&&(r=n)}var T=new Array;T.push(i.nodeName.toLowerCase());var I=new Array;return I.push(p.join(", ")),r?a.RulePass("Pass_0"):a.RulePotential("Potential_1",[T.toString(),I.toString()])}},{id:"HAAC_Aria_Or_HTML5_Attr",context:"dom:*[aria-required], dom:*[aria-autocomplete], dom:*[aria-readonly], dom:*[aria-disabled], dom:*[aria-placeholder]",run:function(e,t){var i=e.dom.node,l=!0;if(i.hasAttribute("required")&&i.hasAttribute("aria-required")&&"false"==i.getAttribute("aria-required").trim().toLowerCase()&&(l=!1),l&&i.hasAttribute("placeholder")&&i.hasAttribute("aria-placeholder")&&(l=!1),l&&i.hasAttribute("aria-autocomplete")){for(var o=i.getAttribute("aria-autocomplete").trim().toLowerCase(),n=i,r=null;null!=n&&"html"!=n.nodeName.toLowerCase()&&!n.hasAttribute("autocomplete");)n=n.parentElement;null!=n&&n.hasAttribute("autocomplete")&&(r=n.getAttribute("autocomplete").trim().toLowerCase()),null!=r&&"on"==r&&"none"==o&&(l=!1)}if(l&&i.hasAttribute("readonly")&&i.hasAttribute("aria-readonly")&&"false"==i.getAttribute("aria-readonly").trim().toLowerCase()&&(l=!1),l&&i.hasAttribute("aria-disabled")){for(var s=i.getAttribute("aria-disabled").trim().toLowerCase(),u=(n=i).hasAttribute("disabled");null!=n&&"html"!=n.nodeName.toLowerCase()&&!n.hasAttribute("disabled");)n=n.parentElement;null!=n&&n.hasAttribute("disabled")&&(u=n.getAttribute("disabled")),1!=u&&""!=u&&"DISABLED"!=u&&"disabled"!=u||"html"==n.nodeName.toLowerCase()||"false"!=s||(l=!1)}return l?a.RulePass("Pass_0"):a.RuleFail("Fail_1")}},{id:"HAAC_Aria_Native_Host_Sematics",context:"dom:*",dependencies:["Rpt_Aria_ValidProperty"],run:function(e,t){var i=e.dom.node,o=!0,n=[];null!==i.getAttribute("role")&&(n=i.getAttribute("role").trim().toLowerCase().split(/\s+/));var r=i.tagName.toLowerCase(),s=[],u=[],d=[],c=[],m=[],p=l.RPTUtil.getElementAriaProperty(i);s=l.RPTUtil.getAllowedAriaRoles(i,p);for(var _=0;_-1&&("img"!==r||!l.RPTUtil.attributeNonEmpty(i,"alt")||"presentation"!==n[_]&&"none"!==n[_]?-1===m.indexOf(n[_])&&m.push(n[_]):-1===d.indexOf(n[_])&&(d.push(n[_]),o=!1));u=l.RPTUtil.getAllowedAriaAttributes(i,m,p),"input"==i.nodeName.toLowerCase()&&l.RPTUtil.attributeNonEmpty(i,"type")&&"password"==i.getAttribute("type").trim().toLowerCase()&&u.push("aria-required");var h=i.attributes;if(h)for(_=0;_1?a.RuleFail("Fail_2"):0===l.RPTUtil.getInnerText(o[0]).trim().length?a.RuleFail("Fail_3"):a.RulePass("Pass_0")}}];t.a11yRulesFieldset=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesInput=void 0;var a=i(0),l=i(1),o=[{id:"WCAG20_Input_ExplicitLabel",context:"aria:button,aria:checkbox,aria:combobox,aria:listbox,aria:menuitemcheckbox,aria:menuitemradio,aria:radio,aria:searchbox,aria:slider,aria:spinbutton,aria:switch,aria:textbox,aria:progressbar,dom:input[type=file],dom:output",run:function(e,t){var i=e.dom.node,o=!0,n=i.nodeName.toLowerCase(),r="text";"input"==n&&i.hasAttribute("type")?r=i.getAttribute("type").toLowerCase():("button"===n||l.RPTUtil.hasRoleInSemantics(i,"button"))&&(r="buttonelem"),"input"==n&&""==r&&(r="text");var s=["text","file","password","checkbox","radio","search","tel","url","email","date","number","range","time","color"],u=["button","reset","submit"];if(-1!==s.indexOf(r)){var d=l.RPTUtil.getLabelForElementHidden(i,!0),c=!1;if(d)if(l.RPTUtil.hasInnerContentHidden(d))c=!0;else if((d.getAttribute("aria-label")||"").trim().length>0)c=!0;else if(d.hasAttribute("aria-labelledby")){var m=d.ownerDocument.getElementById(d.getAttribute("aria-labelledby"));m&&l.RPTUtil.hasInnerContent(m)&&(c=!0)}(o=null!=d&&c||null==d&&l.RPTUtil.attributeNonEmpty(i,"title")||l.RPTUtil.hasAriaLabel(i)||l.RPTUtil.hasImplicitLabel(i))||2+s.indexOf(r)}else if(-1!==u.indexOf(r))-1===["reset","submit"].indexOf(r)||i.hasAttribute("value")?(o=l.RPTUtil.attributeNonEmpty(i,"value")||l.RPTUtil.hasAriaLabel(i))||2+s.length+u.indexOf(r):o=!0;else if("buttonelem"==r){var p=!1;if("img"===i.nodeName.toLowerCase()&&i.hasAttribute("alt")){var _=i.getAttribute("alt");p=0!=_.trim().length||0==_.length}(o=l.RPTUtil.hasInnerContentHidden(i)||l.RPTUtil.hasAriaLabel(i)||p)||2+s.length+u.length+1}if(!o&&-1!==u.indexOf(r)&&i.hasAttribute("class")&&"dijitOffScreen"==i.getAttribute("class")&&i.parentElement.hasAttribute("widgetid")){var h=i.parentElement.getAttribute("widgetid")+"_label",R=i.ownerDocument.getElementById(h);null!=R&&((o=l.RPTUtil.hasInnerContentHidden(R))||2+s.length+u.length+4+u.indexOf(r))}return o||"optgroup"!=n||(o=l.RPTUtil.attributeNonEmpty(i,"label"))||2+s.length+u.length+2,o||"option"!=n||(o=l.RPTUtil.attributeNonEmpty(i,"label")||i.innerHTML.trim().length>0)||2+s.length+u.length+3,o?a.RulePass("Pass_0"):i.hasAttribute("role")&&i.getAttribute("role").trim().length>0?a.RuleFail("Fail_2",i.getAttribute("role").split(" ")):a.RuleFail("Fail_1",[n])}},{id:"WCAG20_Input_ExplicitLabelImage",context:"dom:input",run:function(e,t){var i=e.dom.node;return i.hasAttribute("type")&&"image"==i.getAttribute("type").toLowerCase()?l.RPTUtil.attributeNonEmpty(i,"alt")?a.RulePass("Pass_0"):l.RPTUtil.hasAriaLabel(i)?a.RulePass("Pass_1"):i.hasAttribute("title")&&i.getAttribute("title").length>0?a.RulePass("Pass_2"):a.RuleFail("Fail"):null}},{id:"WCAG20_Input_LabelBefore",context:"dom:input, dom:textarea, dom:select",run:function(e,t){var i=e.dom.node;if("input"==i.nodeName.toLowerCase()&&i.hasAttribute("type")){var o=i.getAttribute("type").toLowerCase();if("text"!=o&&"file"!=o&&"password"!=o)return null}var n=l.RPTUtil.getLabelForElementHidden(i,!0);if(null==n||!l.RPTUtil.hasInnerContentHidden(n))return null;var r=l.RPTUtil.compareNodeOrder(n,i);if(-2==r){for(var s=!1,u=i.previousSibling;!s&&null!=u;)s="#text"==u.nodeName.toLowerCase()&&u.nodeValue.trim().length>0||"span"==u.nodeName.toLowerCase()&&u.textContent.trim().length>0,u=u.previousSibling;if(!s)return a.RuleFail("Fail_1")}else if(-1!=r)return a.RuleFail("Fail_2");return a.RulePass("Pass_0")}},{id:"WCAG20_Input_LabelAfter",context:"dom:input",run:function(e,t){var i=e.dom.node,o="";if(i.hasAttribute("type")&&(o=i.getAttribute("type").toLowerCase()),"checkbox"!=o&&"radio"!=o)return null;var n=l.RPTUtil.getLabelForElementHidden(i,!0);if(null===n||!l.RPTUtil.hasInnerContentHidden(n))return null;var r,s=l.RPTUtil.compareNodeOrder(n,i);if(-2===s){r=!1;var u=new l.NodeWalker(n);for(u.node=i;!r&&u.nextNode();)r="#text"===u.node.nodeName.toLowerCase()&&u.node.nodeValue.trim().length>0||"span"===u.node.nodeName.toLowerCase()&&u.node.textContent.trim().length>0;if(!r)return a.RuleFail("Fail_1")}else if(1!=s)return a.RuleFail("Fail_2");return a.RulePass("Pass_0")}},{id:"WCAG20_Input_InFieldSet",context:"dom:input, dom:textarea, dom:select",run:function(e,t){var i=e.dom.node;if("input"==i.nodeName.toLowerCase()&&i.hasAttribute("type")&&("text"!=(c=i.getAttribute("type").toLowerCase())&&"file"!=c&&"password"!=c))return a.RulePass("Pass_0");if(null!=l.RPTUtil.getAncestor(i,"fieldset"))return a.RulePass("Pass_0");for(var o=l.RPTUtil.getAncestor(i,["form","body"]),n=["input","textarea","select"],r=!0,s=0;r&&s0&&f>0?a.RuleFail("Fail_ControlNameMismatch",[n,"checkbox"===n?"radio":"checkbox",R]):"Radio"===n&&(1===s.numRadios||1===g)||"Checkbox"===n&&1===s.numCheckboxes?null===A?a.RulePass("Pass_LoneNogroup",[n]):a.RulePass("Pass_Grouped",[n]):"Checkbox"===n&&s.numCheckboxes>1&&1===f?null===A?a.RulePotential("Potential_LoneCheckbox",[n]):a.RulePass("Pass_Grouped",[n]):null===A?null!==s.nameToGroup[R]?a.RuleFail("Fail_NotGroupedOtherGrouped",[n,R]):a.RuleFail("Fail_NotGroupedOtherNotGrouped",[n,R]):s.nameToGroup[R]!==A?a.RuleFail("Fail_NotSameGroup",[n,R]):a.RulePass("Pass_Grouped",[n])}return"Radio"===n?null===A?a.RulePass("Pass_RadioNoName",[n]):a.RulePass("Pass_Grouped",[n]):null===A?(s.checkboxByName[""]||[]).length>1?a.RulePotential("Potential_UnnamedCheckbox",[n]):a.RulePass("Pass_LoneNogroup",[n]):a.RulePass("Pass_Grouped",[n])}},{id:"WCAG20_Input_HasOnchange",context:"dom:input, dom:textarea, dom:select",run:function(e,t){var i=e.dom.node;if("input"==i.nodeName.toLowerCase()&&i.hasAttribute("type")){var l=i.getAttribute("type").toLowerCase();if("text"!=l&&"file"!=l&&"password"!=l&&"checkbox"!=l&&"radio"!=l)return a.RulePass("Pass_0")}var o=!i.hasAttribute("onchange");return o?a.RulePass("Pass_0"):o?void 0:a.RulePotential("Potential_1")}},{id:"HAAC_Input_HasRequired",context:"dom:input, dom:textarea, dom:select",run:function(e,t){var i=!0;return e.dom.node.hasAttribute("required")&&(i=!1),i?a.RulePass("Pass_0"):i?void 0:a.RulePotential("Potential_1")}},{id:"HAAC_Input_Placeholder",context:"dom:input[placeholder], dom:textarea[placeholder]",run:function(e,t){var i=e.dom.node;if(i.hasAttribute("type")){var l=i.getAttribute("type").toLowerCase();if("hidden"==l||"button"==l)return a.RulePass("Pass_0")}if(i.hasAttribute("hidden")){var o=i.getAttribute("hidden");if(""==o||"hidden"==o.toLowerCase())return a.RulePass("Pass_0")}if(i.hasAttribute("aria-label"))return a.RulePotential("Potential_1");if(i.hasAttribute("aria-labelledby")&&i.hasAttribute("id")){var n=i.getAttribute("id").trim();return i.getAttribute("aria-labelledby").trim().split(/\s+/).includes(n)?a.RulePotential("Potential_2"):a.RulePass("Pass_0")}return a.RulePass("Pass_0")}},{id:"WCAG21_Input_Autocomplete",context:"dom:input[autocomplete], dom:textarea[autocomplete], dom:select[autocomplete]",run:function(e,t){var i={tokensOnOff:["on","off"],tokenOptionalSection:"section-",tokensOptionalPurpose:["shipping","billing"],tokensMandatoryGroup1_password:["new-password","current-password"],tokensMandatoryGroup1_multiline:["street-address"],tokensMandatoryGroup1_month:["cc-exp"],tokensMandatoryGroup1_numeric:["cc-exp-month","cc-exp-year","transaction-amount","bday-day","bday-month","bday-year"],tokensMandatoryGroup1_date:["bday"],tokensMandatoryGroup1_url:["url","photo"],tokensMandatoryGroup1_text:["name","honorific-prefix","given-name","additional-name","family-name","honorific-suffix","nickname","username","organization-title","organization","address-line1","address-line2","address-line3","address-level4","address-level3","address-level2","address-level1","country","country-name","postal-code","cc-name","cc-given-name","cc-additional-name","cc-family-name","cc-number","cc-csc","cc-type","transaction-currency","language","sex"],tokensMandatoryGroup1_all:["name","honorific-prefix","given-name","additional-name","family-name","honorific-suffix","nickname","username","new-password","current-password","organization-title","organization","street-address","address-line1","address-line2","address-line3","address-level4","address-level3","address-level2","address-level1","country","country-name","postal-code","cc-name","cc-given-name","cc-additional-name","cc-family-name","cc-number","cc-exp","cc-exp-month","cc-exp-year","cc-csc","cc-type","transaction-currency","transaction-amount","language","bday","bday-day","bday-month","bday-year","sex","url","photo"],tokensOptionalGroup2:["home","work","mobile","fax","pager"],tokensMandatoryGroup2_tel:["tel"],tokensMandatoryGroup2_email:["email"],tokensMandatoryGroup2_url:["impp"],tokensMandatoryGroup2_text:["tel-country-code","tel-national","tel-area-code","tel-local","tel-local-prefix","tel-local-suffix","tel-extension"],tokensMandatoryGroup2_all:["tel","tel-country-code","tel-national","tel-area-code","tel-local","tel-local-prefix","tel-local-suffix","tel-extension","email","impp"]},o=e.dom.node,n=!1,r=o.nodeName.toLowerCase();if(!l.RPTUtil.isNodeVisible(o)||l.RPTUtil.isNodeDisabled(o))return null;var s=o.hasAttribute("type")?o.getAttribute("type").trim().toLowerCase():"text",u=o.getAttribute("autocomplete").trim().toLowerCase(),d=u.split(/\s+/);if(0===d.length||0===u.length)return null;var c=[],m=[];if("textarea"===r||"select"===r)c=i.tokensMandatoryGroup1_all,m=i.tokensMandatoryGroup2_all;else{if("input"!==r)return null;switch(s){case"text":case"search":c=i.tokensMandatoryGroup1_text.concat(i.tokensMandatoryGroup1_password,i.tokensMandatoryGroup1_url,i.tokensMandatoryGroup1_numeric,i.tokensMandatoryGroup1_month,i.tokensMandatoryGroup1_date),m=i.tokensMandatoryGroup2_all;break;case"password":c=i.tokensMandatoryGroup1_password;break;case"url":c=i.tokensMandatoryGroup1_url,m=i.tokensMandatoryGroup2_url;break;case"email":m=i.tokensMandatoryGroup2_email;break;case"tel":m=i.tokensMandatoryGroup2_tel;break;case"number":c=i.tokensMandatoryGroup1_numeric;break;case"month":c=i.tokensMandatoryGroup1_month;break;case"date":c=i.tokensMandatoryGroup1_date;break;default:return null}}if(d.includes("on")||d.includes("off"))return 1===d.length?a.RulePass(1):a.RuleFail(2);var p=0;return d[p].startsWith(i.tokenOptionalSection)&&d[p].length>8&&p++,d.length>p&&i.tokensOptionalPurpose.includes(d[p])&&p++,d.length>p&&(c.includes(d[p])?(n=!0,p++):(i.tokensOptionalGroup2.includes(d[p])&&p++,m.includes(d[p])&&(n=!0,p++))),n&&d.length===p?a.RulePass("Pass_0"):a.RuleFail("Fail_1")}},{id:"WCAG20_Input_VisibleLabel",context:"aria:button,aria:checkbox,aria:combobox,aria:listbox,aria:menuitemcheckbox,aria:menuitemradio,aria:radio,aria:searchbox,aria:slider,aria:spinbutton,aria:switch,aria:textbox,aria:progressbar,dom:input[type=file],dom:output",dependencies:["WCAG20_Input_ExplicitLabel"],run:function(e,t){var i=e.dom.node,o=i.nodeName.toLowerCase();if(!l.RPTUtil.isNodeVisible(i)||l.RPTUtil.isNodeDisabled(i))return null;if(l.RPTUtil.getAncestorWithRole(i,"combobox")&&!(l.RPTUtil.hasRoleInSemantics(i,"textbox")||l.RPTUtil.hasRoleInSemantics(i,"searchbox")||"input"===o||"select"===o&&l.RPTUtil.hasRoleInSemantics(i,"combobox")))return null;for(var n=["listbox","tree","grid","dialog"],r=0;r0),!_){var v=["button","cell","checkbox","columnheader","gridcell","heading","link","menuitem","menuitemcheckbox","menuitemradio","option","radio","row","rowgroup","rowheader","switch","tab","tooltip","treeitem"],P=l.RPTUtil.getRoles(i,!0);for(p=0;p0?a.RulePass("Pass_0"):a.RuleFail("Fail_1"):null}},{id:"Rpt_Aria_MultipleFormLandmarks",context:"dom:body",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.getElementsByRoleHidden(i.ownerDocument,"form",!0,!0);return 0===o.length?null:l.RPTUtil.hasUniqueAriaLabelsLocally(o,!1)?a.RulePass(1):a.RuleFail(2)}},{id:"Rpt_Aria_MultipleFormLandmarks_Implicit",context:"aria:form",run:function(e,t){var i=e.dom.node;if("form"===i.getAttribute("role")||i.hasAttribute("aria-label")||i.hasAttribute("aria-labelledby")||i.hasAttribute("title")){var o=l.RPTUtil.getElementsByRoleHidden(i.ownerDocument,"form",!0,!0);if(0===o.length||1===o.length)return null;var n=l.RPTUtil.getCache(i.ownerDocument,"Rpt_Aria_MultipleFormLandmarks_Implicit",null);n||(n=l.RPTUtil.findAriaLabelDupes(o),l.RPTUtil.setCache(i.ownerDocument,"Rpt_Aria_MultipleFormLandmarks_Implicit",n));var r=l.RPTUtil.getAriaLabel(i);return""!==r&&(!(r in n)||n[r]<=1)?a.RulePass("Pass_0"):a.RuleFail("Fail_1",[r])}return null}},{id:"Rpt_Aria_MultipleNavigationLandmarks_Implicit",context:"aria:navigation",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.getElementsByRoleHidden(i.ownerDocument,"navigation",!0,!0);if(0===o.length||1===o.length)return null;var n=l.RPTUtil.getCache(i.ownerDocument,"Rpt_Aria_MultipleNavigationLandmarks_Implicit",null);n||(n=l.RPTUtil.findAriaLabelDupes(o),l.RPTUtil.setCache(i.ownerDocument,"Rpt_Aria_MultipleNavigationLandmarks_Implicit",n));var r=l.RPTUtil.getAriaLabel(i);return""!==r&&(!(r in n)||n[r]<=1)?a.RulePass("Pass_0"):a.RuleFail("Fail_1",[r])}},{id:"Rpt_Aria_MultipleSearchLandmarks",context:"aria:search",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.getElementsByRoleHidden(i.ownerDocument,"search",!0,!0);if(0===o.length||1===o.length)return null;var n=l.RPTUtil.getCache(i.ownerDocument,"Rpt_Aria_MultipleSearchLandmarks",null);n||(n=l.RPTUtil.findAriaLabelDupes(o),l.RPTUtil.setCache(i.ownerDocument,"Rpt_Aria_MultipleSearchLandmarks",n));var r=l.RPTUtil.getAriaLabel(i);return""!==r&&(!(r in n)||n[r]<=1)?a.RulePass("Pass_0"):a.RuleFail("Fail_1",[r])}},{id:"Rpt_Aria_MultipleRegionsUniqueLabel_Implicit",context:"aria:region",run:function(e,t){var i=e.dom.node;if("region"===i.getAttribute("role")||i.hasAttribute("aria-label")||i.hasAttribute("aria-labelledby")||i.hasAttribute("title")){var o=l.RPTUtil.getElementsByRoleHidden(i.ownerDocument,"region",!0,!0);if(0===o.length||1===o.length)return null;var n=l.RPTUtil.getCache(i.ownerDocument,"Rpt_Aria_MultipleRegionsUniqueLabel_Implicit",null);n||(n=l.RPTUtil.findAriaLabelDupes(o),l.RPTUtil.setCache(i.ownerDocument,"Rpt_Aria_MultipleRegionsUniqueLabel_Implicit",n));var r=l.RPTUtil.getAriaLabel(i);return""!==r&&(!(r in n)||n[r]<=1)?a.RulePass("Pass_0"):a.RuleFail("Fail_1",[r])}return null}},{id:"Rpt_Aria_ApplicationLandmarkLabel",context:"aria:application",run:function(e,t){var i=e.dom.node;return l.RPTUtil.hasAriaLabel(i)?a.RulePass("Pass_0"):a.RuleFail("Fail_1")}},{id:"Rpt_Aria_MultipleApplicationLandmarks",context:"aria:application",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.getElementsByRoleHidden(i.ownerDocument,"application",!0,!0);if(0===o.length||1===o.length)return null;var n=l.RPTUtil.getCache(i.ownerDocument,"Rpt_Aria_MultipleApplicationLandmarks",null);n||(n=l.RPTUtil.findAriaLabelDupes(o),l.RPTUtil.setCache(i.ownerDocument,"Rpt_Aria_MultipleApplicationLandmarks",n));var r=l.RPTUtil.getAriaLabel(i);return""!==r&&(!(r in n)||n[r]<=1)?a.RulePass("Pass_0"):a.RuleFail("Fail_1",[r])}},{id:"Rpt_Aria_MultipleDocumentRoles",context:"aria:document",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.getElementsByRoleHidden(i.ownerDocument,"document",!0,!0);if(0===o.length||1===o.length)return null;var n=l.RPTUtil.getCache(i.ownerDocument,"Rpt_Aria_MultipleDocumentRoles",null);n||(n=l.RPTUtil.findAriaLabelDupes(o),l.RPTUtil.setCache(i.ownerDocument,"Rpt_Aria_MultipleDocumentRoles",n));var r=l.RPTUtil.getAriaLabel(i);return""===r||!(r in n)||n[r]<=1?a.RulePass("Pass_0"):a.RuleFail("Fail_1",[r])}},{id:"Rpt_Aria_ArticleRoleLabel_Implicit",context:"aria:article",run:function(e,t){var i=e.dom.node;return l.RPTUtil.hasAriaLabel(i)?a.RulePass("Pass_0"):a.RuleFail("Fail_1")}},{id:"Rpt_Aria_MultipleArticleRoles_Implicit",context:"aria:article",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.getElementsByRoleHidden(i.ownerDocument,"article",!0,!0);if(0===o.length||1===o.length)return null;var n=l.RPTUtil.getCache(i.ownerDocument,"Rpt_Aria_MultipleArticleRoles_Implicit",null);n||(n=l.RPTUtil.findAriaLabelDupes(o),l.RPTUtil.setCache(i.ownerDocument,"Rpt_Aria_MultipleArticleRoles_Implicit",n));var r=l.RPTUtil.getAriaLabel(i);return""===r||!(r in n)||n[r]<=1?a.RulePass("Pass_0"):a.RuleFail("Fail_1",[r])}},{id:"Rpt_Aria_GroupRoleLabel_Implicit",context:"dom:*[role], dom:details",run:function(e,t){var i=e.dom.node;if(!l.RPTUtil.hasRoleInSemantics(i,"group"))return null;var o=l.RPTUtil.hasAriaLabel(i);if(!o&&(o=l.RPTUtil.getAncestorWithRole(i,"menubar")||l.RPTUtil.getAncestorWithRole(i,"menu")||l.RPTUtil.getAncestorWithRole(i,"tree")))return null;var n=new Array;n.push(i.nodeName.toLowerCase());var r=new Array;return r.push("group"),o?a.RulePass("Pass_0"):a.RuleFail("Fail_1",[n.toString(),r.toString()])}},{id:"Rpt_Aria_MultipleGroupRoles_Implicit",context:"aria:group",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.getElementsByRoleHidden(i.ownerDocument,"group",!0,!0);if(0===o.length||1===o.length)return null;var n=l.RPTUtil.getCache(i.ownerDocument,"Rpt_Aria_MultipleGroupRoles_Implicit",null);n||(n=l.RPTUtil.findAriaLabelDupes(o),l.RPTUtil.setCache(i.ownerDocument,"Rpt_Aria_MultipleGroupRoles_Implicit",n));var r=l.RPTUtil.getAriaLabel(i);return""===r||!(r in n)||n[r]<=1?a.RulePass("Pass_0"):a.RuleFail("Fail_1",[r])}},{id:"Rpt_Aria_WidgetLabels_Implicit",context:"dom:*",run:function(e,t){var i=e.dom.node;if(i.hasAttribute("type")&&"hidden"==i.getAttribute("type")||l.RPTUtil.getAncestorWithRole(i,"combobox")&&!l.RPTUtil.hasRoleInSemantics(i,"textbox")&&!l.RPTUtil.hasRoleInSemantics(i,"searchbox"))return null;if(-1!=["input","textarea","select","button","datalist","optgroup","option","keygen","output","progress","meter"].indexOf(i.nodeName.toLowerCase()))return null;if(("link"===i.nodeName.toLowerCase()||"a"===i.nodeName.toLowerCase()||"area"===i.nodeName.toLowerCase())&&i.hasAttribute("href"))return null;for(var n=["listbox","tree","grid","dialog"],r=0;r=0&&(_=l.RPTUtil.hasInnerContentOrAlt(i)),_||(_=l.RPTUtil.hasImplicitLabel(i)))}return 0===A?null:_?a.RulePass("Pass_0"):a.RuleFail("Fail_1")}},{id:"Rpt_Aria_MultipleToolbarUniqueLabel",context:"aria:toolbar",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.getElementsByRoleHidden(i.ownerDocument,"toolbar",!0,!0);if(0===o.length||1===o.length)return null;var n=l.RPTUtil.getCache(i.ownerDocument,"Rpt_Aria_MultipleToolbarUniqueLabel",null);n||(n=l.RPTUtil.findAriaLabelDupes(o),l.RPTUtil.setCache(i.ownerDocument,"Rpt_Aria_MultipleToolbarUniqueLabel",n));var r=l.RPTUtil.getAriaLabel(i);return""!==r&&(!(r in n)||n[r]<=1)?a.RulePass("Pass_0"):a.RuleFail("Fail_1",[r])}}];t.a11yRulesLabeling=n},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesFig=void 0;var a=i(0),l=i(1),o=[{id:"HAAC_Figure_label",context:"dom:figure",run:function(e,t){for(var i=e.dom.node,o=!1,n=i.getElementsByTagName("figcaption"),r=0;!o&&r0&&l.RPTUtil.isNodeVisible(n[r])&&(o=!0);return o||(o=i.hasAttribute("aria-labelledby")&&i.getAttribute("aria-labelledby").trim().length>0),o?a.RulePass("Pass_0"):a.RuleFail("Fail_1",[])}}];t.a11yRulesFig=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesLabel=void 0;var a=i(0),l=i(1),o=[{id:"RPT_Label_UniqueFor",context:"dom:label[for]",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.getCache(i.ownerDocument,"RPT_Label_Single",{}),n=i.getAttribute("for"),r=!(n in o);return o[n]=!0,r?a.RulePass("Pass_0"):a.RuleFail("Fail_1")}},{id:"Valerie_Label_HasContent",context:"dom:label",run:function(e,t){var i=e.dom.node;if(l.RPTUtil.hasInnerContentHidden(i))return a.RulePass("Pass_Regular");if((i.getAttribute("aria-label")||"").trim().length>0)return a.RulePass("Pass_AriaLabel");if(i.hasAttribute("aria-labelledby")){var o=i.ownerDocument.getElementById(i.getAttribute("aria-labelledby"));if(o&&l.RPTUtil.hasInnerContent(o))return a.RulePass("Pass_LabelledBy")}return a.RuleFail("Fail_1")}},{id:"WCAG20_Label_RefValid",context:"dom:label[for]",run:function(e,t){var i=e.dom.node,o=i.getAttribute("for"),n=!1,r=i.ownerDocument.getElementById(o);if(r){if(n=!0,!r.hasAttribute("role")){var s=r.nodeName.toLowerCase();if(n="input"==s||"select"==s||"textarea"==s||"button"==s||"datalist"==s||"optgroup"==s||"option"==s||"keygen"==s||"output"==s||"progress"==s||"meter"==s||"fieldset"==s||"legend"==s,"input"==r.nodeName.toLowerCase()&&r.hasAttribute("type")){var u=r.getAttribute("type").toLowerCase();n="text"==u||"password"==u||"file"==u||"checkbox"==u||"radio"==u||"hidden"==u||"search"==u||"tel"==u||"url"==u||"email"==u||"date"==u||"number"==u||"range"==u||"image"==u||"time"==u||"color"==u||"datetime"==u||"month"==u||"week"==u}}n&&!l.RPTUtil.isNodeVisible(r)&&(n=!1)}var d=[];return n||d.push(o),n?a.RulePass("Pass_0"):a.RuleFail("Fail_1",d)}},{id:"WCAG20_Label_TargetInvisible",context:"dom:label[for]",run:function(e,t){var i=e.dom.node,o=!0,n=i.getAttribute("for"),r=i.ownerDocument.getElementById(n);return r&&(o="hidden"!=l.RPTUtil.getElementAttribute(r,"type")),o?a.RulePass("Pass_0"):o?void 0:a.RulePotential("Potential_1")}},{id:"WCAG21_Label_Accessible",context:"aria:button,aria:checkbox,aria:gridcell,aria:link,aria:menuitem,aria:menuitemcheckbox,aria:menuitemradio,aria:option,aria:radio,aria:switch,aria:tab,aria:treeitem,dom:input,dom:textarea,dom:select,dom:output,dom:meter",run:function(e,t){var i=e.dom.node;if(!l.RPTUtil.isNodeVisible(i)||l.RPTUtil.isNodeDisabled(i))return null;var o=!0,n=i.nodeName.toLowerCase(),r=!1,s=null;"input"===n&&i.hasAttribute("type")&&(s=i.getAttribute("type").toLowerCase(),-1!==["button","reset","submit"].indexOf(s)&&(r=!0));var u=l.RPTUtil.getAriaAttribute(i,"aria-labelledby");if(u&&!r);else{var d=null;if(u)for(var c=u.split(/\s+/),m=0;m1){var P=v.indexOf(b);if(P>=0&&v.length>b.length){var y=/^[0-9a-zA-Z]+$/;if(P+b.length0)v.charAt(P-1).match(y)&&(o=!1)}-1===P&&(o=!1)}}return o?a.RulePass("Pass_0"):a.RuleFail("Fail_1")}}];t.a11yRulesLabel=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesTable=void 0;var a=i(0),l=i(1),o=[{id:"WCAG20_Table_CapSummRedundant",context:"dom:table",run:function(e,t){var i,o=e.dom.node;if(o.hasAttribute("summary"))0,i=o.getAttribute("summary").trim().toLowerCase();else if(o.hasAttribute("aria-describedby")){1;var n=o.getAttribute("aria-describedby");if(n){var r=o.ownerDocument.getElementById(n);r&&(i=l.RPTUtil.getInnerText(r).trim().toLowerCase())}}if(!i)return null;var s=o.getElementsByTagName("caption");return 0===s.length?null:i.length>0?i!=l.RPTUtil.getInnerText(s[0]).trim().toLowerCase()?a.RulePass("Pass_0"):a.RuleFail("Fail_1"):void 0}},{id:"Valerie_Caption_HasContent",context:"dom:caption",run:function(e,t){var i=e.dom.node;return l.RPTUtil.hasInnerContentHidden(i)?a.RulePass("Pass_0"):a.RuleFail("Fail_1")}},{id:"Valerie_Caption_InTable",context:"dom:caption",run:function(e,t){var i=e.dom.node;return null!=l.RPTUtil.getAncestor(i,"table")?a.RulePass("Pass_0"):a.RuleFail("Fail_1")}},{id:"WCAG20_Table_SummaryAria3",context:"dom:table",run:function(e,t){var i=e.dom.node,o=!l.RPTUtil.isComplexDataTable(i)||l.RPTUtil.attributeNonEmpty(i,"summary")||i.hasAttribute("aria-describedby"),n=i.getAttribute("aria-describedby");if(n){var r=i.ownerDocument.getElementById(n);o=!!r&&!!l.RPTUtil.getInnerText(r).trim().toLowerCase()}return o?a.RulePass("Pass_0"):o?void 0:a.RulePotential("Potential_1")}},{id:"RPT_Table_DataHeadingsAria",context:"dom:table",run:function(e,t){var i=e.dom.node,o=i.rows;return l.RPTUtil.isDataTable(i)&&null!=o&&0!=o.length?l.RPTUtil.isTableHeaderInFirstRowOrColumn(i)?a.RulePass("Pass_0"):a.RuleFail("Fail_1"):null}},{id:"Valerie_Table_DataCellRelationships",context:"dom:td, dom:th",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.getAncestor(i,"table");if(null==o||!l.RPTUtil.isComplexDataTable(o))return null;if(null===l.RPTUtil.getCache(i,"Valerie_Table_DataCellRelationships",null)){for(var n=[],r=0;r=1?a.RulePass("Pass_0"):a.RulePotential("Potential_1",[n])}},{id:"IBMA_Focus_MultiTab",context:"aria:button,aria:link,aria:menuitem,aria:spinbutton,aria:tablist,aria:combobox,aria:listbox,aria:menu,aria:radiogroup,aria:tree,aria:checkbox,aria:option,aria:radio,aria:slider,aria:spinbutton,aria:textbox,aria:columnheader,aria:rowheader,aria:slider,aria:tab",run:function(e,t){var i=e.dom.node,n=o.ARIAMapper.nodeToRole(i),r=0;if(l.RPTUtil.isTabbable(i)&&++r,r<2&&i.firstChild)for(var s=new l.NodeWalker(i);r<2&&s.nextNode()&&s.node!=i;)1==s.node.nodeType&&!s.bEndTag&&l.RPTUtil.isTabbable(s.node)&&++r;return r<2?a.RulePass("Pass_0"):a.RulePotential("Potential_1",[n])}}];t.a11yRulesFocus=n},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesList=void 0;var a=i(0),l=i(1),o=[{id:"RPT_List_Misuse",context:"dom:dl, dom:ul, dom:ol, dom:dir, dom:menu, dom:li, dom:dd, dom:dt",run:function(e,t){var i,o=e.dom.node,n=o.nodeName.toLowerCase();o.children;if("dl"==n){var r="",s="",u=o.firstChild;i=!0;var d=!1;if(o.children&&0!=o.children.length){for(;i&&null!=u;){if(1==u.nodeType){var c=u.nodeName.toLowerCase();if(l.RPTUtil.isPresentationalElement(u)){d=!0,u=u.nextSibling;continue}i="dd"==c||"dt"==c,""==r&&(r=c),s=c}u=u.nextSibling}i=i&&"dt"==r&&"dd"==s||i&&d}else i=!0}else if("li"==n)i=null!=l.RPTUtil.getAncestor(o,["ul","ol","dir","menu"]);else if("dd"==n||"dt"==n)i=null!=l.RPTUtil.getAncestor(o,"dl");else{u=o.firstChild;if("ul"!=n&&"ol"!=n&&"menu"!=n||o.children&&0!=o.children.length){var m=!1;d=!1;for(i=!0;i&&null!=u;)l.RPTUtil.isPresentationalElement(u)?(d=!0,u=u.nextSibling):(i=1!=u.nodeType||"li"==u.nodeName.toLowerCase()||"template"==u.nodeName.toLowerCase()&&("ul"==n||"ol"==n),m=m||1==u.nodeType&&"li"==u.nodeName.toLowerCase(),u=u.nextSibling);i=i&&m||i&&d}else i=!0}return i?a.RulePass("Pass_0"):i?void 0:a.RulePotential("Potential_1")}},{id:"RPT_List_UseMarkup",context:"dom:*",run:function(e,t){for(var i=e.dom.node,o=!0,n=i.firstChild;o&&n;){if("#text"==n.nodeName){var r=n.nodeValue;if(!(o=!/^[ \t\r\n]*[( ]*[1-9]*[\*\-).][ \t][A-Z,a-z]+/.test(r)))for(var s=new l.NodeWalker(n);!o&&s.prevNode();){var u=s.node.nodeName.toLowerCase();if(["blockquote","center","dir","div","form","h1","h2","h3","h4","h5","h6","hr","br","menu","p","pre"].includes(u))break;if("#text"==u){var d=s.node.nodeValue;o=d.length>0&&![" ","\t","\n"].includes(d.charAt(d.length-1))}}}n=n.nextSibling}if(!o){var c=l.RPTUtil.getAncestor(i,["body","script"]);o=null==c||"body"!=c.nodeName.toLowerCase()}return o?a.RulePass("Pass_0"):o?void 0:a.RulePotential("Potential_1")}}];t.a11yRulesList=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesText=void 0;var a=i(0),l=i(1),o=[{id:"RPT_Text_SensoryReference",context:"dom:*",run:function(e,t){var i={value:["top-left","top-right","bottom-right","bottom-left","round","square","shape","rectangle","triangle","right","left","above","below","top","bottom","upper","lower","corner","beside"],type:"[string]"},o=e.dom.node,n=o.nodeName.toLowerCase();if("style"===n||"link"===n)return a.RulePass(1);var r=null,s=l.RPTUtil.getCache(o.ownerDocument,"RPT_Text_SensoryReference",null);if(null==s){for(var u=i.value,d="("+u[0],c=1;c0&&null!=e.match(s)){var t={},a=[],l=["right-click","left-click","right-clicking","right-clicks","left-clicking","left-clicks"];e.split(/[\n\r ]+/).reduce((function(e,o){for(var n=i.value,r=o.replace(/[.?!:;()'",`\]]+$/,""),s=o.toLowerCase().replace(/[.?!:;()'",`\]]/g,""),u=0;u0){var A=s.charAt(d-1);R="-"==A||"."==A||"?"==A||"!"==A||":"==A||";"==A||"("==A||")"==A||"'"==A||'"'==A||","==A||".`"==A||"\\"==A||"]"==A}(s.length==p||1==h||1==R)&&c<0&&(m=!1,t.hasOwnProperty(r)||(t[r]=!0,a.push(r)),u=n.length)}}return e[r]=(e[r]||0)+1,e}),Object.create(null));r=a.join(", ")}}p=p.nextSibling};m&&p;)_();if(!m){var h=l.RPTUtil.getAncestor(o,["body","script"]);m=null==h||"body"!=h.nodeName.toLowerCase()}return m?a.RulePass("Pass_0"):a.RulePotential("Potential_1",[r])}},{id:"WCAG20_Text_Emoticons",context:"dom:*",run:function(e,t){for(var i=e.dom.node,o=[":-)",":)",":o)",":]",":3",":c)",":>","=]","8)","=)",":D","C:",":-D",":D","8D","XD","=D","=3","<=3","<=8","--!--",":-(",":(",":c",":<",":[","D:","D8","D;","D=","DX","v.v",":-9",";-)",";)","*)",";]",";D",":-P",":P",":-p",":p","=p",":-Þ",":Þ",":-b",":b",":-O",":O","O_O","o_o","8O","OwO","O-O","0_o","O_o","O3O","o0o ;o_o;","o...o","0w0",":-/",":/",":\\","=/","=\\",":S",":|","d:-)","qB-)",":)~",":-)>....",":-X",":X",":-#",":#","O:-)","0:3","O:)",":'(",";*(","T_T","TT_TT","T.T",":-*",":*","^o)",">:)",">;)",">:-)","B)","B-)","8)","8-)","^>.>^","^<.<^","^>_>^","^<_<^","D:<",">:(","D-:<",">:-(",":-@[1]",";(","`_´","D<","<3","<333","=^_^=","=>.>=","=<_<=","=>.<=","\\,,/","\\m/","\\m/\\>.0)for(var u=0;n&&u0&&!/\s/.test(r.substring(d-1,d))||d0||l.RPTUtil.getAncestor(i,"code")))return a.RulePass("Pass_0");for(var o=!0,n=l.RPTUtil.getInnerText(i),r=0,s=0,u="",d=0;o&&d=":\/\\-]/.test(c)?u==c?s+=1:s=0:(/\s/.test(c)||(r+=1),s=0),u=c,(r>=5||s>=4)&&(o=!1)}return o?a.RulePass("Pass_0"):o?void 0:a.RulePotential("Potential_1")}}];t.a11yRulesText=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesBlockquote=void 0;var a=i(0),l=i(1),o=[{id:"RPT_Blockquote_HasCite",context:"dom:blockquote",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.attributeNonEmpty(i,"cite");if(!o){var n=l.RPTUtil.getDocElementsByTag(i,"cite");o=null!=n&&n.length>0}return o?a.RulePass("Pass_0"):o?void 0:a.RulePotential("Potential_1")}},{id:"RPT_Blockquote_WrapsTextQuote",context:"dom:*",run:function(e,t){for(var i=e.dom.node,o=!0,n=i.firstChild,r=null;o&&n;){if("#text"==n.nodeName){var s=n.nodeValue,u=(s=s.replace(/(\S)'(\S)/g,"$1$2")).match(/("[^"]+")/g),d=s.match(/('[^']+')/g);if((null!=u||null!=d)&&null==l.RPTUtil.getAncestor(n,["blockquote","q","script"])){if(null!=u)for(var c=0;o&&c0?a.RulePass("Pass_0"):a.RuleFail("Fail_3")}},{id:"RPT_Title_Valid",context:"dom:head dom:title",run:function(e,t){var i=e.dom.node,o=l.RPTUtil.getInnerText(i).trim(),n=o.toLowerCase();return n.includes(".com")||n.includes(".net")||n.includes(".org")?a.RulePass("Pass_0",[o]):0===o.length?null:!/^\S*\.[a-zA-Z]{1,4}(?!.)|^https?:\/\/\S*/i.test(o)?a.RulePass("Pass_0",[o]):a.RulePotential("Potential_2")}}];t.a11yRulesTitle=n},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesBody=void 0;var a=i(0),l=i(1),o=i(7),n=[{id:"WCAG20_Body_FirstASkips_Native_Host_Sematics",context:"dom:body",run:function(e,t,i){if(o.AncestorUtil.isPresentationFrame(i))return null;var n,r=e.dom.node,s=r.ownerDocument;if(null===l.RPTUtil.getCache(r,"IBM_hasLandmarks_Implicit",null)&&l.RPTUtil.setCache(r,"IBM_hasLandmarks_Implicit",l.RPTUtil.getElementsByRoleHidden(r.ownerDocument,["application","banner","complementary","contentinfo","form","main","navigation","search"],!0,!0).length>0),!(n=l.RPTUtil.getCache(r,"IBM_hasLandmarks_Implicit",!1))){for(var u=l.RPTUtil.getDocElementsByTag(r,"a"),d=null,c=0;c0),!(i=l.RPTUtil.getCache(n,"IBM_hasLandmarks_Implicit",!1))){var s=r.links;if(s&&s.length>0&&l.RPTUtil.isNodeVisible(s[0]))for(var u=l.RPTUtil.getInnerText(r.links[0]).toLowerCase(),d=0;!i&&d0&&-1==s.indexOf(n);return u?a.RulePass("Pass_0"):u?void 0:a.RulePotential("Potential_1")}},{id:"RPT_Media_VideoObjectTrigger",context:"dom:embed, dom:object",run:function(e,t){var i=e.dom.node,o=!l.RPTUtil.isVideoObjEmbedLink(i);return o?null:o?void 0:a.RuleManual("Manual_1")}},{id:"RPT_Media_ImgColorUsage",context:"dom:embed, dom:object, dom:img, dom:applet, dom:script, dom:style, dom:input, dom:link,dom:*[style], dom:font[color], dom:tr[bgcolor], dom:th[bgcolor], dom:td[bgcolor]",run:function(e,t){var i=e.dom.node,o=!1;if(!i.hasAttribute("style")){var n=i.nodeName.toLowerCase();if("input"==n)o=!i.hasAttribute("type")||"image"!=i.getAttribute("type").toLowerCase();else if("link"==n)o=!i.hasAttribute("rel")||"stylesheet"!=i.getAttribute("rel").toLowerCase(),o=l.RPTUtil.triggerOnce(i.ownerDocument,"RPT_Media_ImgColorUsage_Links",o);else if("embed"==n||"object"==n){var r;if(i.hasAttribute("type"))o=(r=i.getAttribute("type")).startsWith("text")||r.startsWith("audio");if(!o&&i.hasAttribute("codetype"))o=(r=i.getAttribute("codetype")).startsWith("text")||r.startsWith("audio");if(!o){var s=i.getAttribute("embed"==n?"src":"data");null==s&&(s="");var u=l.RPTUtil.getFileExt(s);o=u.length>0&&l.RPTUtil.isAudioExt(u)}}else"script"==n&&(o=l.RPTUtil.triggerOnce(i.ownerDocument,"RPT_Media_ImgColorUsage_Scripts",o))}return o?a.RulePass("Pass_0"):o?void 0:a.RulePotential("Potential_1")}},{id:"HAAC_Media_DocumentTrigger2",context:"dom:a[href],dom:area[href]",run:function(e,t){var i=e.dom.node.getAttribute("href"),o=l.RPTUtil.getFileExt(i),n=![".docx",".doc",".pdf",".odt"].includes(o);return n?null:n?void 0:a.RuleManual("Manual_1")}}];t.a11yRulesMedia=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesVideo=void 0;var a=i(0),l=[{id:"HAAC_Video_HasNoTrack",context:"dom:video",run:function(e,t){for(var i=e.dom.node,l=!1,o=i.getElementsByTagName("track"),n=0;n0)for(n=0;n0&&(n=!0),r=r.nextSibling;for(var s=!1,u=i;u;)"body"===u.nodeName.toLowerCase()&&(s=!0),u=u.parentElement;if(!n||!s)return null;var d=l.RPTUtil.ColorCombo(i).fg.toHex();return-1!==o.indexOf(d)?a.RulePass(1,[d]):a.RuleFail(2,[d])}},{id:"DESIGN_COLOR_Palette_Background",context:"dom:*",run:function(e,t){for(var i=e.dom.node,n=i.parentElement;n;){if(l.RPTUtil.getCache(n,"DESIGN_COLOR_Palette_Background",null))return null;n=n.parentElement}var r=l.RPTUtil.ColorCombo(i).bg.toHex();return-1!==o.indexOf(r)?a.RulePass(1,[r]):(l.RPTUtil.setCache(i,"DESIGN_COLOR_Palette_Background",!0),a.RuleFail(2,[r]))}}];t.designRulesColor=n},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.designRulesGrid=void 0;var a=i(0),l=[{id:"DESIGN_GridLayout_ImgAspectRatio",context:"aria:img",run:function(e,t){var i=e.dom.node,l=i.naturalWidth,o=i.naturalHeight,n=Math.max(l/o,o/l);return Math.abs(n-16/9)<1e-7||Math.abs(n-4/3)<1e-7||Math.abs(n-1.5)<1e-7||Math.abs(n-2)<1e-7||Math.abs(n-1)<1e-7?a.RulePass(1):a.RuleFail(2)}}];t.designRulesGrid=l},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.designRulesType=void 0;var a=i(0),l=[{id:"DESIGN_Typography_Plex",context:"dom:*",run:function(e,t){for(var i=e.dom.node,l=!1,o=i;o;){if("style"===o.nodeName.toLowerCase()||"script"===o.nodeName.toLowerCase()){l=!1;break}"body"===o.nodeName.toLowerCase()&&(l=!0),o=o.parentElement}for(var n=!1,r=i.firstChild;r;)3===r.nodeType&&r.nodeValue.trim().length>0&&(n=!0),r=r.nextSibling;if(!l||!n)return null;var s=i.ownerDocument.defaultView.getComputedStyle(i).fontFamily;return'"IBM Plex'===s.substring(0,'"IBM Plex'.length)?a.RulePass(1,[s]):a.RuleFail(2,[s])}},{id:"DESIGN_Typography_TextAlignLeft",context:"dom:*",run:function(e,t){for(var i=e.dom.node,l=!1,o=i;o;)"body"===o.nodeName.toLowerCase()&&(l=!0),o=o.parentElement;var n=(i.innerText||"").trim().length>0;if(!l||!n)return null;var r=i.ownerDocument.defaultView.getComputedStyle(i).textAlign;return r&&"left"!==r&&"start"!==r?a.RuleFail(2):a.RulePass(1)}}];t.designRulesType=l},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.checkNls=void 0;var a=i(62),l=i(63),o={};function n(e){for(var t in e)o[t]=e[t]}t.checkNls=o,n(a.a11yNls),n(l.designNls)},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yNls=void 0;t.a11yNls={RPT_List_Misuse:{0:"List elements should only be used for lists of related items",Pass_0:"Rule Passed",Potential_1:"List element is missing or improperly structured"},RPT_Marquee_Trigger:{0:"The element is obsolete and should not be used",Passed_0:"Rule Passed",Fail_1:"Scrolling content found that uses the obsolete element"},RPT_Headers_FewWords:{0:"Heading elements must not be used for presentation",Pass_0:"Rule Passed",Potential_1:"Verify that the heading element is a genuine heading"},WCAG20_Input_ExplicitLabelImage:{0:'The element of type "image" should have a text alternative',Pass_0:"Image button provides alternative text using the 'alt' attribute",Pass_1:"Image button provides alternative text using a WAI-ARIA label",Pass_2:"Image button provides alternative text using the 'title' attribute",Fail:'The element of type "image" has no text alternative'},RPT_Img_UsemapValid:{0:"Server-side image map hot-spots must have duplicate text links",Pass_0:"Rule Passed",Potential_1:"Server-side image map hot-spots do not have duplicate text links"},WCAG20_Object_HasText:{0:" elements must have a text alternative for the content rendered by the object",Pass_0:"Rule Passed",Fail_1:"An element does not have a text alternative"},WCAG20_Applet_HasAlt:{0:" elements must provide an 'alt' attribute and an alternative description",Pass_0:"Rule Passed",Fail_1:"An element does not have an 'alt' attribute that provides a short text alternative",Fail_2:"The 'alt' attribute value for an element duplicates the 'code' attribute",Fail_3:"An element provides alternative text, but does not provide inner content"},RPT_Media_AudioTrigger:{0:"Audio information should also be available in text form",Pass_0:"Rule Passed",Manual_1:"Provide transcripts for audio files"},RPT_Blockquote_HasCite:{0:"Use
only for quotations, not indentation",Pass_0:"Rule Passed",Potential_1:"Verify that
is used only for quotations, not indentation"},RPT_Meta_Refresh:{0:"Pages should not refresh automatically",Pass_0:"Rule Passed",Potential_1:"Verify page is not being caused to refresh automatically"},WCAG20_Frame_HasTitle:{0:"Inline frames must have a unique, non-empty 'title' attribute",Pass_0:"Rule Passed",Fail_1:"Inline frame does not have a 'title' attribute"},WCAG20_Input_ExplicitLabel:{0:"Each form control must have an associated label",Pass_0:"Rule Passed",Fail_1:"Form control element <{0}> has no associated label",Fail_2:'Form control with "{0}" role has no associated label'},RPT_Media_AltBrief:{0:"Alternative text in 'alt' attribute should be brief (<150 characters)",Pass_0:"Rule Passed",Potential_1:"Text alternative is more than 150 characters"},WCAG20_A_TargetAndText:{0:"Users should be warned in advance if their input action will open a new window or otherwise change their context",Pass_0:"Rule Passed",Potential_1:"Inform the user when their input action will open a new window or otherwise change their context"},WCAG20_Area_HasAlt:{0:" elements in an image map must have a text alternative",Pass_0:"Rule Passed",Fail_1:" element in an image map has no text alternative"},RPT_Media_ImgColorUsage:{0:"Do not use color as the only means to convey information, provide an additional non-color cue",Pass_0:"Rule Passed",Potential_1:"Verify color is not the only means to convey information"},WCAG20_Meta_RedirectZero:{0:"Page should not automatically refresh without warning or option to turn it off or adjust the time limit",Pass_0:"Rule Passed",Fail_1:"Check page does not automatically refresh without warning or options"},RPT_Elem_Deprecated:{0:"Avoid use of obsolete language features if possible",Pass_0:"Rule Passed",Potential_1:"Obsolete language features are being used"},RPT_Blockquote_WrapsTextQuote:{0:"Quotations should be marked with or
elements",Pass_0:"Rule Passed",Potential_1:"If the following text is a quotation, mark it as a or
element: {0}"},RPT_Elem_EventMouseAndKey:{0:"All interactive content with mouse event handlers must have equivalent keyboard access",Pass_0:"Rule Passed",Manual_1:"Confirm the <{0}> element with mouse event handler(s) '{1}' has a corresponding keyboard handler(s)"},WCAG20_Doc_HasTitle:{0:"The page should have a title that correctly identifies the subject of the page",Pass_0:"Rule Passed",Fail_1:"Missing element so there can be no element present",Fail_2:"Missing <title> element in <head> element",Fail_3:"The <title> element is empty (no innerHTML)"},RPT_Block_ShouldBeHeading:{0:"Heading text must use a heading element",Pass_0:"Rule Passed",Potential_1:"Check text that looks like a heading but is not within a heading element"},WCAG20_Form_HasSubmit:{0:"A <form> element should have a submit button or an image button",Pass_0:"Rule Passed",Potential_1:"Verify the <form> element has a submit button or an image button"},RPT_Elem_UniqueId:{0:"Element 'id' attribute values must be unique within a document",Pass_0:"Rule Passed",Fail_1:'The <{0}> element has the id "{1}" that is empty',Fail_2:'The <{0}> element has the id "{1}" that is already in use'},RPT_Font_ColorInForm:{0:"Combine color and descriptive markup to indicate required form fields",Pass_0:"Rule Passed",Potential_1:"Check color is not used as the only visual means to convey which fields are required"},RPT_Label_UniqueFor:{0:"Form controls should have exactly one label",Pass_0:"Rule Passed",Fail_1:"Form control has more than one label"},RPT_Img_AltCommonMisuse:{0:"'alt' attribute value must be a good inline replacement for the image",Pass_0:"Rule Passed",Potential_1:"Verify that the file name serves as a good inline replacement for the image"},RPT_Img_LongDescription2:{0:" The 'longdesc' attribute must reference HTML content",Pass_0:"Rule Passed",Potential_1:"Verify that the file designated by the 'longdesc' attribute contains valid HTML content (file extension not recognized)"},WCAG20_Img_HasAlt:{0:"Images must have an 'alt' attribute with a short text alternative if they convey meaning, or 'alt=\"\" if decorative",Pass_0:"Rule Passed",Fail_1:"Image 'alt' attribute value consists only of whitespace",Fail_2:"Image does not have an 'alt' attribute short text alternative"},RPT_Style_BackgroundImage:{0:"Images included by using CSS alone must not convey important information",Pass_0:"Rule Passed",Potential_1:"Verify the CSS background image does not convey important information"},RPT_Pre_ASCIIArt:{0:"ASCII art must have a text alternative",Pass_0:"Rule Passed",Potential_1:"Verify that ASCII art has a text alternative"},RPT_Media_VideoReferenceTrigger:{0:"Pre-recorded media should have an audio track that describes visual information",Pass_0:"Rule Passed",Manual_1:"Verify availability of a user-selectable audio track with description of visual content"},RPT_Media_AudioVideoAltFilename:{0:"Audio or video on the page must have a short text alternative that describes the media content",Pass_0:"Rule Passed",Potential_1:"Filename used as label for embedded audio or video"},RPT_Style_ColorSemantics1:{0:"Combine color and descriptive markup to convey information",Pass_0:"Rule Passed",Potential_1:"Verify color is not used as the only visual means of conveying information"},WCAG20_Select_HasOptGroup:{0:"Groups of related options within a selection list should be grouped with <optgroup>",Pass_0:"Rule Passed",Potential_1:"Group of related options may need <optgroup>"},RPT_List_UseMarkup:{0:"Use proper HTML list elements to create lists",Pass_0:"Rule Passed",Potential_1:"Verify whether this is a list that should use HTML list elements"},RPT_Script_OnclickHTML1:{0:"Scripts should not be used to emulate links",Pass_0:"Rule Passed",Potential_1:"Possible use of a script to emulate a link"},WCAG20_Table_Structure:{0:'Table elements with \'role="presentation" or \'role="none" should not have structural elements or attributes',Pass_0:"Rule Passed",Fail_1:'The <{0}> element with "presentation" role or "none" role has structural element(s) and/or attribute(s) \'{1}\''},WCAG20_Img_AltTriggerNonDecorative:{0:"Convey information with text rather than images of text",Pass_0:"Rule Passed",Potential_1:"Verify that images of text are not used to convey information"},WCAG20_Blink_AlwaysTrigger:{0:"Content that blinks persistently must not be used",Pass_0:"Rule Passed",Fail_1:"Content found that blinks persistently"},RPT_Blink_CSSTrigger1:{0:"Do not use the \"blink\" value of the 'text-decoration' property for longer than five seconds",Pass_0:"Rule Passed",Potential_1:"Check the \"blink\" value of the CSS 'text-decoration' property is not used for more than than five seconds"},RPT_Html_SkipNav:{0:"Provide a way to bypass blocks of content that are repeated on multiple Web pages",Pass_0:"Rule Passed",Potential_1:"Verify there is a way to bypass blocks of content that are repeated on multiple Web pages"},RPT_Title_Valid:{0:"Page <title> should be a descriptive title, rather than a filename",Pass_0:"Rule Passed",Fail_1:"Page <title> is empty",Potential_2:"Verify that using the filename as the page <title> value is descriptive"},RPT_Header_HasContent:{0:"Heading elements must provide descriptive text",Pass_0:"Rule Passed",Fail_1:"Heading element has no descriptive content"},WCAG20_Html_HasLang:{0:"Page must identify the default language of the document with a 'lang' attribute",Pass_0:'Page language detected as "{0}"',Fail_1:"Page detected as XHTML 1.0, but has neither 'lang' nor 'xml:lang' attributes",Fail_2:"Page detected as XHTML, but does not have an 'xml:lang' attribute",Fail_3:"Page detected as HTML, but does not have a 'lang' attribute",Fail_4:"Page detected as XHTML 1.0 with 'lang' and 'xml:lang' attributes that do not match: \"{0}\", \"{1}\"",Potential_5:"Page detected as XHTML 1.0 with only a 'lang' attribute. Confirm that page is only delivered via text/html mime type",Potential_6:"Page detected as XHTML 1.0 with only an 'xml:lang' attribute. Confirm that page is only delivered via xml mime type"},WCAG20_Form_TargetAndText:{0:"User should be informed in advance when interacting with content causes a change of context",Pass_0:"Rule Passed",Potential_1:"Verify that interacting with content will not open pop-up windows or change the active window without informing the user"},WCAG20_A_HasText:{0:"Hyperlinks must have a text description of their purpose",Pass_0:"Hyperlink has a description of its purpose",Fail_1:"Hyperlink has no link text, label or image with a text alternative"},WCAG20_Fieldset_HasLegend:{0:" <fieldset> elements must have a single, non-empty <legend> as a label",Pass_0:"Rule Passed",Fail_1:"<fieldset> element does not have a <legend>",Fail_2:"<fieldset> element has more than one <legend>",Fail_3:"<fieldset> element <legend> is empty"},RPT_Media_VideoObjectTrigger:{0:"Live media (streaming video with audio) should have captions for audio content",Pass_0:"Rule Passed",Manual_1:"Verify captions are provided for live media (streaming video with audio)"},RPT_Text_SensoryReference:{0:"Instructions must be meaningful without shape or location words",Pass_0:"Rule Passed",Potential_1:"If the word(s) '{0}' is part of instructions for using page content, check it is still understandable without this location or shape information"},RPT_Embed_AutoStart:{0:"Mechanism must be available to pause or stop and control the volume of the audio that plays automatically",Pass_0:"Rule Passed",Potential_1:"Verify there is a mechanism to pause or stop and control the volume for the audio that plays automatically"},RPT_Style_HinderFocus1:{0:"The keyboard focus indicator must be highly visible when default border or outline is modified by CSS",Pass_0:"Rule Passed",Potential_1:"Check the keyboard focus indicator is highly visible when using CSS elements for border or outline"},WCAG20_Elem_Lang_Valid:{0:"The language of content must be specified in accordance with BCP 47",Pass_0:"Rule Passed",Fail_1:"Specified language is not valid BCP 47"},WCAG20_Img_LinkTextNotRedundant:{0:"The text alternative for an image within a link should not repeat the link text or adjacent link text",Pass_0:"Rule Passed",Fail_1:"Link text is repeated in an image 'alt' value within the same link",Fail_2:"Link text of previous link is repeated in image 'alt' value of a link",Fail_3:"Image 'alt' value within a link is repeated in link text of the link after"},RPT_Style_ExternalStyleSheet:{0:"Check external style sheets to ensure that CSS is not used to add images that convey important information in the content.",Pass_0:"Rule Passed",Potential_1:"External style sheets detected ensure that CSS does not add images that convey important information in the content."},RPT_Header_Trigger:{0:"Heading text should correctly describe the subject of the web page sections",Pass_0:"Rule Passed",Potential_1:"Verify that heading text correctly describes the subject of each web page section"},RPT_Script_OnclickHTML2:{0:"Scripts should not be used to emulate links",Pass_0:"Rule Passed",Potential_1:"Verify that 'onclick' events are not used in script to emulate a link"},WCAG20_Table_CapSummRedundant:{0:"The table summary must not duplicate the caption",Pass_0:"Rule Passed",Fail_1:"The table summary duplicates the caption"},WCAG20_Input_LabelBefore:{0:"Text inputs and <select> elements must have a label before the input control",Pass_0:"Rule Passed",Fail_1:"Text input is nested in label, so label is not before the text input control",Fail_2:"Label text is located after its associated text input or <select> element"},WCAG20_Input_LabelAfter:{0:"Checkboxes and radio buttons must have a label after the input control",Pass_0:"Rule Passed",Fail_1:"Checkbox or radio button is nested in label, so label is not after the input control",Fail_2:"Label text is located before its associated checkbox or radio button element"},WCAG20_Embed_HasNoEmbed:{0:"<embed> elements should be immediately followed by a non-embedded element",Pass_0:"Rule Passed",Potential_1:"Verify that the <embed> element is immediately followed by a non-embedded element"},WCAG20_Table_Scope_Valid:{0:'Value for \'scope\' attribute must be "row", "col", "rowgroup", or "colgroup"',Pass_0:"Rule Passed",Fail_1:"Value provided is invalid for the 'scope' attribute"},WCAG20_Img_TitleEmptyWhenAltNull:{0:"When the image 'alt' attribute is empty, the 'title' attribute must also be empty",Pass_0:"Rule Passed",Fail_1:"The image 'alt' attribute is empty, but the 'title' attribute is not empty"},WCAG20_Input_InFieldSet:{0:"Groups of logically related input elements should be contained within a <fieldset> element",Pass_0:"Rule Passed",Potential_1:"Use the <fieldset> element to group logically related input elements"},WCAG20_Input_RadioChkInFieldSet:{0:"Related sets of radio buttons or checkboxes should be programmatically grouped",Pass_LoneNogroup:"{0} grouping not required for a control of this type",Pass_Grouped:"{0} input is grouped with other related controls with the same name",Pass_RadioNoName:"Radio input is not grouped, but passes because it has no name to group with other radio inputs",Fail_ControlNameMismatch:'{0} input found that has the same name, "{2}" as a {1} input',Potential_LoneCheckbox:"Verify that this ungrouped checkbox input is not related to other checkboxes",Potential_UnnamedCheckbox:"Verify that this un-named, ungrouped checkbox input is not related to other checkboxes",Fail_NotGroupedOtherGrouped:'{0} input is not in the group with another {0} with the name "{1}"',Fail_NotGroupedOtherNotGrouped:'{0} input and others with the name "{1}" are not grouped together',Fail_NotSameGroup:'{0} input is in a different group than another {0} with the name "{1}"'},WCAG20_Select_NoChangeAction:{0:"No changes of context should occur when a selection value receives focus",Pass_0:"Rule Passed",Potential_1:"Verify that no change of context or action occurs when selection options in this component receive focus"},WCAG20_Input_HasOnchange:{0:"Verify that any changes of context are explained in advance to the user",Pass_0:"Rule Passed",Potential_1:"Verify that any changes of context are explained in advance to the user"},RPT_Embed_HasAlt:{0:"Provide alternative content for <embed> elements",Pass_0:"Rule Passed",Potential_1:"Verify that the <embed> element has alternative content"},Valerie_Noembed_HasContent:{0:"<noembed> elements should contain descriptive text",Pass_0:"Rule Passed",Potential_1:"Add descriptive text to the <noembed> element"},Valerie_Caption_HasContent:{0:"A <caption> element for a <table> element must contain descriptive text",Pass_0:"Rule Passed",Fail_1:"The <table> element has an empty <caption> element"},Valerie_Caption_InTable:{0:"The <caption> element must be nested inside the associated <table> element",Pass_0:"Rule Passed",Fail_1:"<caption> element is not nested inside a <table> element"},Valerie_Label_HasContent:{0:"A <label> element must have non-empty descriptive text that identifies the purpose of the interactive component",Pass_Regular:"<label> element has accessible name with inner content",Pass_AriaLabel:"<label> element has accessible name via 'aria-label'",Pass_LabelledBy:"<label> element has accessible name via 'aria-labelledby'",Fail_1:"The <label> element does not have descriptive text that identifies the expected input"},Valerie_Elem_DirValid:{0:'\'dir\' attribute value must be "ltr", "rtl", or "auto"',Pass_0:"Rule Passed",Fail_1:"Invalid value used for the 'dir' attribute"},Valerie_Frame_SrcHtml:{0:"A <frame> containing non-HTML content must be made accessible",Pass_0:"Rule Passed",Potential_1:"Verify <frame> content is accessible"},Valerie_Table_DataCellRelationships:{0:"For a complex data table, all <th> and <td> elements must be related via 'header' or 'scope' attributes",Pass_0:"Rule Passed",Fail_1:"Complex table does not have headers for each cell properly defined with 'header' or 'scope'"},RPT_Table_LayoutTrigger:{0:"Avoid using tables to format text documents in columns unless the table can be linearized",Pass_0:"Rule Passed",Potential_1:"Verify table is not being used to format text content in columns unless the table can be linearized"},RPT_Table_DataHeadingsAria:{0:"Data table must identify headers",Pass_0:"Rule Passed",Fail_1:"Table has no headers identified"},WCAG20_Label_RefValid:{0:"The 'for' attribute must reference a non-empty, unique 'id' attribute of an <input> element",Pass_0:"Rule Passed",Fail_1:"The value \"{0}\" of the 'for' attribute is not the 'id' of a valid <input> element"},WCAG20_Elem_UniqueAccessKey:{0:"'accesskey' attribute values on each element must be unique for the page",Pass_0:"Rule Passed",Fail_1:"'accesskey' attribute value on the element is not unique"},WCAG20_Script_FocusBlurs:{0:"Scripting must not remove focus from content that normally receives focus",Pass_0:"Rule Passed",Potential_1:"Verify script does not remove focus from content that normally receives focus"},HAAC_Img_UsemapAlt:{0:"An image map and each <area> element in an image map must have text alternative(s)",Pass_0:"Rule Passed",Fail_1:"Image map or child <area> has no text alternative"},WCAG20_Text_Emoticons:{0:"Emoticons must have a short text alternative that describes their purpose",Pass_0:"Rule Passed",Potential_1:"Verify that emoticons have a text alternative"},WCAG20_Style_BeforeAfter:{0:"Do not use CSS '::before' and '::after' pseudo-elements to insert non-decorative content",Pass_0:"Rule Passed",Potential_1:"Verify the '::before' and '::after' pseudo-elements do not insert non-decorative content"},WCAG20_Text_LetterSpacing:{0:"Use CSS 'letter-spacing' to control spacing within a word",Pass_0:"Rule Passed",Potential_1:"Verify space characters are not being used to create space between the letters of a word"},Rpt_Aria_ValidRole:{0:"Elements must have a valid 'role' per WAI-ARIA specification",Pass_0:"Rule Passed",Fail_1:"The 'role' defined on the element is not valid per WAI-ARIA specification"},Rpt_Aria_ValidPropertyValue:{0:"WAI-ARIA property values must be valid",Pass_0:"Rule Passed",Fail_1:"The value \"{0}\" specified for attribute '{1}' on element <{2}> is not valid"},Rpt_Aria_ValidIdRef:{0:"The WAI-ARIA property must reference a non-empty unique id of an existing element that is visible",Pass_0:"Rule Passed",Fail_1:"The 'id' \"{0}\" specified for the WAI-ARIA property '{1}' value is not valid"},Rpt_Aria_RequiredProperties:{0:"When using a WAI-ARIA role on an element, the required attributes for that role must be defined",Pass_0:"Rule Passed",Fail_1:"An element with WAI-ARIA role '{0}' does not have the required WAI-ARIA attribute(s): '{1}'"},Rpt_Aria_EmptyPropertyValue:{0:"When specifying a required WAI-ARIA attribute, the value must not be empty",Pass_0:"Rule Passed",Fail_1:"The element attribute(s): '{0}' value is empty"},Rpt_Aria_ValidProperty:{0:"WAI-ARIA attributes must be valid for the element's role",Pass_0:"Rule Passed",Fail_1:"The attribute(s) '{0}' referenced by the element <{1}> is not a valid WAI-ARIA state or property"},Rpt_Aria_InvalidTabindexForActivedescendant:{0:"Element using 'aria-activedescendant' property must have its 'tabindex' attribute value set to 0 or -1 to be keyboard accessible",Pass_0:"Rule Passed",Fail_1:"The <{0}> element using 'aria-activedescendant' set to \"{1}\" does not have its 'tabindex' attribute value set to 0 or -1"},Rpt_Aria_MissingFocusableChild:{0:"UI component must have at least one focusable child element for keyboard access",Pass_0:"Rule Passed",Fail_1:'The descendent <{0}> element with "{1}" role has no focusable child element'},Rpt_Aria_MissingKeyboardHandler:{0:"Interactive WAI_ARIA UI components must provide keyboard access",Pass_0:"Rule Passed",Potential_1:'Verify the <{0}> element with "{1}" role has keyboard access'},WCAG20_Img_PresentationImgHasNonNullAlt:{0:'Image designated as decorative must have \'alt=""',Pass_0:"Rule Passed",Fail_1:"Image designated as decorative has non-null 'alt' attribute"},Rpt_Aria_MultipleSearchLandmarks:{0:'Each element with "search" role must have a unique label that describes its purpose',Pass_0:"Rule Passed",Fail_1:'Multiple elements with "search" role do not have unique labels'},Rpt_Aria_MultipleApplicationLandmarks:{0:'Each element with "application" role must have a unique label that describes its purpose',Pass_0:"Rule Passed",Fail_1:'Multiple elements with "application" role do not have unique labels'},Rpt_Aria_ApplicationLandmarkLabel:{0:'An element with "application" role must have a label that describes its purpose',Pass_0:"Rule Passed",Fail_1:'Element with "application" role does not have a label'},Rpt_Aria_MultipleDocumentRoles:{0:'All elements with a "document" role must have unique labels',Pass_0:"Rule Passed",Fail_1:'Multiple elements with a "document" role do not have unique labels'},WCAG20_Label_TargetInvisible:{0:'Do not label hidden <input> elements (\'type="hidden")',Pass_0:"Rule Passed",Potential_1:'Hidden <input> element (\'type="hidden") is possibly labelled'},HAAC_Video_HasNoTrack:{0:"Media embedded with a <video> element must have a <track> element to provide text alternatives for audio or visual content",Pass_0:"Rule Passed",Potential_1:"Media embedded with a <video> element does not have a <track> element that can provide text alternatives for audio or visual content"},HAAC_Audio_Video_Trigger:{0:"Media using <audio> and/or <video> elements must have keyboard accessible controls",Pass_0:"Rule Passed",Manual_1:"Verify media using <audio> and/or <video> elements have keyboard accessible controls"},HAAC_Input_HasRequired:{0:"If the application must be accessible in Internet Explorer 8, use 'aria-required' instead of the HTML5 'required' property",Pass_0:"Rule Passed",Potential_1:"If the application must be accessible in Internet Explorer 8, use 'aria-required' instead of the HTML5 'required' property"},HAAC_Aria_ImgAlt:{0:'An element with "img" role must have a non-empty label',Pass_0:"Rule Passed",Fail_1:'Element with "img" role has no label',Fail_2:'Element with "img" role has no label or an empty label',Fail_3:"Element with \"img\" role missing non-empty 'aria-label' or 'aria-labelledby'"},HAAC_BackgroundImg_HasTextOrTitle:{0:"Background images that convey important information must have a text alternative that describes the image",Pass_0:"Rule Passed",Manual_1:"Verify important background image information has a text alternative in system high contrast mode"},HAAC_Accesskey_NeedLabel:{0:"An HTML element with an assigned 'accesskey' attribute must have an associated label",Pass_0:"Rule Passed",Potential_1:"The HTML element with an assigned 'accesskey' attribute does not have an associated label"},HAAC_Aria_Or_HTML5_Attr:{0:"HTML5 attributes must not conflict with the associated WAI-ARIA attribute used on an input element",Pass_0:"Rule Passed",Fail_1:"HTML5 attribute is in conflict with the associated WAI-ARIA attribute used on an input element"},HAAC_Canvas:{0:"The <canvas> element may not be accessible",Pass_0:"Rule Passed",Manual_1:"Verify accessibility of the <canvas> element"},HAAC_Figure_label:{0:"A <figure> element must have an associated label",Pass_0:"Rule Passed",Fail_1:"The <figure> element does not have an associated label"},HAAC_Input_Placeholder:{0:"HTML5 'placeholder' attribute must not be used as a visible label replacement",Pass_0:"Rule Passed",Potential_1:"HTML5 placeholder is the only visible label",Potential_2:"Additional visible label referenced by 'aria-labelledby' is not valid"},HAAC_Aria_Native_Host_Sematics:{0:"WAI-ARIA roles and attributes must be valid for the element they are assigned to",Pass_0:"Rule Passed",Fail_1:"The WAI-ARIA role or attribute '{0}' is not valid for the element <{1}>"},RPT_Form_ChangeEmpty:{0:"A form should not be submitted automatically without warning the user",Pass_0:"Rule Passed",Potential_1:"Confirm the form does not submit automatically without warning"},IBMA_Color_Contrast_WCAG2AA:{0:"The contrast ratio of text with its background must meet WCAG 2.1 AA requirements",Pass_0:"Rule Passed",Fail_1:"Text contrast of {0} with its background is less than the WCAG AA minimum requirements for text of size {1}px and weight of {2}"},IBMA_Color_Contrast_WCAG2AA_PV:{0:"The contrast ratio of text with its background (i.e. background with a color gradient or a background image) must meet WCAG 2.1 AA requirements",Pass_0:"Rule Passed",Potential_1:"Verify the contrast ratio of the text against the lightest and the darkest colors of the background meets the WCAG 2.1 AA minimum requirements for text of size {1}px and weight of {2}"},WCAG20_Body_FirstASkips_Native_Host_Sematics:{0:"Pages must provide a way to skip directly to the main content",Pass_0:"Rule Passed",Fail_1:'The page does not provide a way to quickly navigate to the main content (WAI-ARIA "main" landmark or a skip link)'},WCAG20_Body_FirstAContainsSkipText_Native_Host_Sematics:{0:"The description of a hyperlink used to skip content must communicate where it links to",Pass_0:"Rule Passed",Potential_1:"Verify that if this hyperlink skips content, the description communicates where it links to"},Rpt_Aria_RequiredChildren_Native_Host_Sematics:{0:"An element with a WAI-ARIA role must contain required children",Pass_0:"Rule Passed",Potential_1:'The element with WAI-ARIA role of "{0}" does not contain or own at least one child element with each of the following WAI-ARIA roles: "{1}"'},Rpt_Aria_RequiredParent_Native_Host_Sematics:{0:"An element with a WAI-ARIA role must be contained within a valid element",Pass_0:"Rule Passed",Fail_1:'The element with "{0}" role is not contained in or owned by an element with one of the following WAI-ARIA roles: "{1}"'},Rpt_Aria_EventHandlerMissingRole_Native_Host_Sematics:{0:"Elements with event handlers must have a valid WAI-ARIA role",Pass_0:"Rule Passed",Fail_1:"The <{0}> element with '{1}' does not have a valid WAI-ARIA role specified"},Rpt_Aria_WidgetLabels_Implicit:{0:"Interactive component must have a programmatically associated name",Pass_0:"Rule Passed",Fail_1:"Interactive component does not have a programmatically associated name"},Rpt_Aria_OrphanedContent_Native_Host_Sematics:{0:"All content must reside within an element with a landmark role",Pass_0:"Rule Passed",Fail_1:"Content is not within a landmark element"},Rpt_Aria_RegionLabel_Implicit:{0:'Each element with "region" role must have a label that describes its purpose',Pass_0:"Rule Passed",Fail_1:"Section element with an implicit \"region\" role is not labeled with an 'aria-label' or 'aria-labelledby'",Fail_2:"The element with \"region\" role is not labeled with an 'aria-label' or 'aria-labelledby'"},Rpt_Aria_MultipleMainsVisibleLabel_Implicit:{0:'Each element with "main" role should have a unique visible label that describes its purpose',Pass_0:"Rule Passed",Fail_1:'Multiple elements with "main" role do not have unique visible labels'},Rpt_Aria_MultipleBannerLandmarks_Implicit:{0:'Each element with "banner" role must have a unique label that describes its purpose',Pass_0:"Rule Passed",Fail_1:'Multiple elements with "banner" role do not have unique labels'},Rpt_Aria_MultipleComplementaryLandmarks_Implicit:{0:'Each element with "complementary" role must have a unique label that describes its purpose',Pass_0:"Rule Passed",Fail_1:'Multiple elements with "complementary" role do not have unique labels'},Rpt_Aria_MultipleContentinfoLandmarks_Implicit:{0:'Each element with "contentinfo" role must have a unique label that describes its purpose',Pass_0:"Rule Passed",Fail_1:'Multiple elements with "contentinfo" role do not have unique labels'},Rpt_Aria_MultipleFormLandmarks_Implicit:{0:'Each element with "form" role must have a unique label that describes its purpose',Pass_0:"Rule Passed",Fail_1:'Multiple elements with "form" role do not have unique labels'},Rpt_Aria_MultipleNavigationLandmarks_Implicit:{0:'Each element with "nav" role must have a unique label that describes its purpose',Pass_0:"Rule Passed",Fail_1:'Multiple elements with "nav" role do not have unique labels'},Rpt_Aria_ComplementaryLandmarkLabel_Implicit:{0:'Each element with "complementary" role should have a visible label that describes its purpose',Pass_0:"Rule Passed",Fail_1:'The element with "complementary" role does not have a visible label'},Rpt_Aria_MultipleArticleRoles_Implicit:{0:'Each element with "article" role must have a unique label that describes its purpose',Pass_0:"Rule Passed",Fail_1:'Multiple elements with "article" role do not have unique labels'},Rpt_Aria_ArticleRoleLabel_Implicit:{0:'An element with "article" role must have a label that describes its purpose',Pass_0:"Rule Passed",Fail_1:'The element with "article" role does not have a label'},Rpt_Aria_MultipleGroupRoles_Implicit:{0:'Each element with "group" role must have a unique label that describes its purpose',Pass_0:"Rule Passed",Fail_1:'Multiple elements with "group" role do not have unique labels'},Rpt_Aria_GroupRoleLabel_Implicit:{0:'An element with "group" role should have a unique label that describes its purpose',Pass_0:"Rule Passed",Fail_1:'The <{0}> element with "group" role does not have a label'},Rpt_Aria_MultipleContentinfoInSiblingSet_Implicit:{0:'A page, document or application should only have one element with "contentinfo" role',Pass_0:"Rule Passed",Fail_1:'Multiple elements with "contentinfo" role found on a page'},Rpt_Aria_OneBannerInSiblingSet_Implicit:{0:'There must be only one element with "banner" role on the page',Pass_0:"Rule Passed",Fail_1:'There is more than one element with "banner" role on the page'},Rpt_Aria_ContentinfoWithNoMain_Implicit:{0:'An element with "contentinfo" role is only permitted with an element with "main" role',Pass_0:"Rule Passed",Fail_1:'The element with "contentinfo" role is present without an element with "main" role'},Rpt_Aria_ComplementaryRequiredLabel_Implicit:{0:'An element with "complementary" role must have a label',Pass_0:"Rule Passed",Fail_1:'The element with "complementary" role does not have a label'},Rpt_Aria_MultipleRegionsUniqueLabel_Implicit:{0:'Each element with a "region" role must have a unique label',Pass_0:"Rule Passed",Fail_1:'Multiple elements with "region" role do not have unique labels'},IBMA_Focus_Tabbable:{0:"Component must have at least one tabbable element",Pass_0:"Rule Passed",Potential_1:'Component with "{0}" role does not have a tabbable element'},IBMA_Focus_MultiTab:{0:"Certain components must have no more than one tabbable element",Pass_0:"Rule Passed",Potential_1:'Component with "{0}" role has more than one tabbable element'},WCAG20_Table_SummaryAria3:{0:"Complex data tables should have a 'summary' or an 'aria-describedby' that references an overview of the table",Pass_0:"Rule Passed",Potential_1:"A complex data table should have a 'summary' or an 'aria-describedby' that references an overview of the table"},RPT_Style_Trigger2:{0:"Windows high contrast mode must be supported for CSS background images",Pass_0:"Rule Passed",Manual_1:"Confirm Windows high contrast mode is supported for CSS background images"},Rpt_Aria_MultipleMainsRequireLabel_Implicit_2:{0:'Elements with "main" role must have unique labels',Pass_0:"Rule Passed",Fail_1:'Multiple elements with "main" role do not have unique labels'},HAAC_Media_DocumentTrigger2:{0:"File download mechanisms should be keyboard-operable and preserve page focus location",Pass_0:"Rule Passed",Manual_1:"Verify that the file download mechanism does not cause a keyboard trap"},HAAC_Aria_ErrorMessage:{0:"A custom error message must reference a valid 'id' value and when triggered the message must be appropriately exposed",Pass_0:"Rule Passed",Fail_1:"Custom error message has invalid reference 'id' value",Fail_2:"Custom error message is not visible"},HAAC_List_Group_ListItem:{0:'List component with "group" role must limit children to <listitem> elements',Pass_0:"Rule Passed",Fail_1:'List component with "group" role has children that are not <listitem> elements'},HAAC_ActiveDescendantCheck:{0:"The 'aria-activedescendant' property must reference the 'id' of a non-empty, non-hidden active child element",Pass_0:"Rule Passed",Fail_1:"The 'aria-activedescendant' property is empty",Fail_2:"The 'aria-activedescendant' property references a hidden node",Fail_3:"Element is not a combobox, and the referenced active-descendant element is not a valid descendant",Fail_4:"Element is a combobox, and the referenced active-descendant element is not controlled by this component"},HAAC_Application_Role_Text:{0:'Non-decorative static text and image content within an element with "application" role must be accessible',Pass_0:"Rule Passed",Potential_1:'Verify that the non-decorative static text and image content within an element with "application" role are accessible'},Rpt_Aria_MultipleToolbarUniqueLabel:{0:"All toolbar components on a page must have unique labels specified",Pass_0:"Rule Passed",Fail_1:"Multiple toolbar components do not have unique labels"},HAAC_Combobox_ARIA_11_Guideline:{0:"Combobox component must be coded to WAI-ARIA 1.2 Authoring Practices",Pass_0:"Rule Passed",Manual_1:"Confirm combobox component is coded to WAI-ARIA 1.2 Authoring Practices"},HAAC_Combobox_Must_Have_Text_Input:{0:"A combobox must have a single line text input element with a valid role",Pass_0:"Rule Passed",Fail_1:"The text input element or number of lines is not valid for the combobox"},HAAC_Combobox_DOM_Focus:{0:"Initial DOM focus on the combobox must be set on the text input",Pass_0:"Rule Passed",Fail_1:"Initial DOM focus on the combobox is not set on the text input when the combobox receives focus"},HAAC_Combobox_Autocomplete:{0:"A combobox that supports autocompletion behavior must have the 'aria-autocomplete' attribute only on its text input element",Pass_0:"Rule Passed",Fail_1:"The combobox has the 'aria-autocomplete' attribute set on an element that isn't a text input"},HAAC_Combobox_Autocomplete_Invalid:{0:"The 'aria-autocomplete' attribute value for a combobox text input element must not be \"inline\"",Pass_0:"Rule Passed",Fail_1:"The 'aria-autocomplete' attribute value of \"inline\" is not valid for the combobox"},HAAC_Combobox_Expanded:{0:"The 'aria-owns' or the 'aria-controls' attribute of the expanded combobox must reference a valid popup 'id' value",Pass_0:"Rule Passed",Fail_1:"The 'aria-owns' or the 'aria-controls' attribute of the expanded combobox does not reference a valid popup 'id' value"},HAAC_Combobox_Popup:{0:"The value of the combobox 'aria-haspopup' attribute must match the 'role' value of the popup element",Pass_0:"Rule Passed",Fail_1:"The value of the combobox 'aria-haspopup' attribute does not match the 'role' value of the popup element"},WCAG21_Style_Viewport:{0:"Text must scale up to 200% without loss of content or functionality",Pass_0:"Rule Passed",Potential_1:"Verify that text sized using viewport units can be resized up to 200%"},WCAG21_Label_Accessible:{0:"Accessible name must match or contain the visible label text",Pass_0:"Rule Passed",Fail_1:"Accessible name does not match or contain the visible label text"},WCAG21_Input_Autocomplete:{0:"The 'autocomplete' attribute's token(s) must be appropriate for the input form field",Pass_0:"Rule Passed",Fail_1:"The 'autocomplete' attribute's token(s) are not appropriate for the input form field"},WCAG20_Input_VisibleLabel:{0:"An input element must have an associated visible label",Pass_0:"Rule Passed",Potential_1:"The input element does not have an associated visible label"}}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.designNls=void 0;t.designNls={DESIGN_COLOR_Palette_Foreground:["Foreground color {0} is in the standard palette.","Foreground color {0} is not in the standard palette."],DESIGN_COLOR_Palette_Background:["Background color {0} is in the standard palette.","Background color {0} is not in the standard palette."],DESIGN_Typography_Plex:['Font Family "{0}" begins with "IBM Plex"','Font Family "{0}" is not a standard font, it does not begin with "IBM Plex".'],DESIGN_Typography_TextAlignLeft:["Text is left justified","Text is not left justified"],DESIGN_GridLayout_ImgAspectRatio:["Rule Passed","Image is not a supported aspect ratio (16:9, 4:3, 3:2, 2:1, or 1:1)"]}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.checkHelp=void 0;var a=i(65),l={};t.checkHelp=l,function(e){for(var t in e)l[t]=e[t]}(a.a11yHelp)},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yHelp=void 0;var a=i(4),l={RPT_List_Misuse:{0:a.Config.helpRoot+"/RPT_List_Misuse",Pass_0:a.Config.helpRoot+"/RPT_List_Misuse",Potential_1:a.Config.helpRoot+"/RPT_List_Misuse"},RPT_Marquee_Trigger:{0:a.Config.helpRoot+"/RPT_Marquee_Trigger",Passed_0:a.Config.helpRoot+"/RPT_Marquee_Trigger",Fail_1:a.Config.helpRoot+"/RPT_Marquee_Trigger"},RPT_Headers_FewWords:{0:a.Config.helpRoot+"/RPT_Headers_FewWords",Pass_0:a.Config.helpRoot+"/RPT_Headers_FewWords",Potential_1:a.Config.helpRoot+"/RPT_Headers_FewWords"},WCAG20_Input_ExplicitLabelImage:{0:a.Config.helpRoot+"/WCAG20_Input_ExplicitLabelImage",Pass_0:a.Config.helpRoot+"/WCAG20_Input_ExplicitLabelImage",Potential_1:a.Config.helpRoot+"/WCAG20_Input_ExplicitLabelImage"},RPT_Img_UsemapValid:{0:a.Config.helpRoot+"/RPT_Img_UsemapValid",Pass_0:a.Config.helpRoot+"/RPT_Img_UsemapValid",Potential_1:a.Config.helpRoot+"/RPT_Img_UsemapValid"},WCAG20_Object_HasText:{0:a.Config.helpRoot+"/WCAG20_Object_HasText",Pass_0:a.Config.helpRoot+"/WCAG20_Object_HasText",Fail_1:a.Config.helpRoot+"/WCAG20_Object_HasText"},WCAG20_Applet_HasAlt:{0:a.Config.helpRoot+"/WCAG20_Applet_HasAlt",Pass_0:a.Config.helpRoot+"/WCAG20_Applet_HasAlt",Fail_1:a.Config.helpRoot+"/WCAG20_Applet_HasAlt",Fail_2:a.Config.helpRoot+"/WCAG20_Applet_HasAlt",Fail_3:a.Config.helpRoot+"/WCAG20_Applet_HasAlt"},RPT_Media_AudioTrigger:{0:a.Config.helpRoot+"/RPT_Media_AudioTrigger",Pass_0:a.Config.helpRoot+"/RPT_Media_AudioTrigger",Manual_1:a.Config.helpRoot+"/RPT_Media_AudioTrigger"},RPT_Blockquote_HasCite:{0:a.Config.helpRoot+"/RPT_Blockquote_HasCite",Pass_0:a.Config.helpRoot+"/RPT_Blockquote_HasCite",Potential_1:a.Config.helpRoot+"/RPT_Blockquote_HasCite"},RPT_Meta_Refresh:{0:a.Config.helpRoot+"/RPT_Meta_Refresh",Pass_0:a.Config.helpRoot+"/RPT_Meta_Refresh",Potential_1:a.Config.helpRoot+"/RPT_Meta_Refresh"},WCAG20_Frame_HasTitle:{0:a.Config.helpRoot+"/WCAG20_Frame_HasTitle",Pass_0:a.Config.helpRoot+"/WCAG20_Frame_HasTitle",Fail_1:a.Config.helpRoot+"/WCAG20_Frame_HasTitle"},WCAG20_Input_ExplicitLabel:{0:a.Config.helpRoot+"/WCAG20_Input_ExplicitLabel",Pass_0:a.Config.helpRoot+"/WCAG20_Input_ExplicitLabel",Fail_1:a.Config.helpRoot+"/WCAG20_Input_ExplicitLabel",Fail_2:a.Config.helpRoot+"/WCAG20_Input_ExplicitLabel"},RPT_Media_AltBrief:{0:a.Config.helpRoot+"/RPT_Media_AltBrief",Pass_0:a.Config.helpRoot+"/RPT_Media_AltBrief",Potential_1:a.Config.helpRoot+"/RPT_Media_AltBrief"},WCAG20_A_TargetAndText:{0:a.Config.helpRoot+"/WCAG20_A_TargetAndText",Pass_0:a.Config.helpRoot+"/WCAG20_A_TargetAndText",Potential_1:a.Config.helpRoot+"/WCAG20_A_TargetAndText"},WCAG20_Area_HasAlt:{0:a.Config.helpRoot+"/WCAG20_Area_HasAlt",Pass_0:a.Config.helpRoot+"/WCAG20_Area_HasAlt",Fail_1:a.Config.helpRoot+"/WCAG20_Area_HasAlt"},RPT_Media_ImgColorUsage:{0:a.Config.helpRoot+"/RPT_Media_ImgColorUsage",Pass_0:a.Config.helpRoot+"/RPT_Media_ImgColorUsage",Potential_1:a.Config.helpRoot+"/RPT_Media_ImgColorUsage"},WCAG20_Meta_RedirectZero:{0:a.Config.helpRoot+"/WCAG20_Meta_RedirectZero",Pass_0:a.Config.helpRoot+"/WCAG20_Meta_RedirectZero",Fail_1:a.Config.helpRoot+"/WCAG20_Meta_RedirectZero"},RPT_Elem_Deprecated:{0:a.Config.helpRoot+"/RPT_Elem_Deprecated",Pass_0:a.Config.helpRoot+"/RPT_Elem_Deprecated",Potential_1:a.Config.helpRoot+"/RPT_Elem_Deprecated"},RPT_Blockquote_WrapsTextQuote:{0:a.Config.helpRoot+"/RPT_Blockquote_WrapsTextQuote",Pass_0:a.Config.helpRoot+"/RPT_Blockquote_WrapsTextQuote",Potential_1:a.Config.helpRoot+"/RPT_Blockquote_WrapsTextQuote"},RPT_Elem_EventMouseAndKey:{0:a.Config.helpRoot+"/RPT_Elem_EventMouseAndKey",Pass_0:a.Config.helpRoot+"/RPT_Elem_EventMouseAndKey",Manual_1:a.Config.helpRoot+"/RPT_Elem_EventMouseAndKey"},WCAG20_Doc_HasTitle:{0:a.Config.helpRoot+"/WCAG20_Doc_HasTitle",Pass_0:a.Config.helpRoot+"/WCAG20_Doc_HasTitle",Fail_1:a.Config.helpRoot+"/WCAG20_Doc_HasTitle",Fail_2:a.Config.helpRoot+"/WCAG20_Doc_HasTitle",Fail_3:a.Config.helpRoot+"/WCAG20_Doc_HasTitle"},RPT_Block_ShouldBeHeading:{0:a.Config.helpRoot+"/RPT_Block_ShouldBeHeading",Pass_0:a.Config.helpRoot+"/RPT_Block_ShouldBeHeading",Potential_1:a.Config.helpRoot+"/RPT_Block_ShouldBeHeading"},WCAG20_Form_HasSubmit:{0:a.Config.helpRoot+"/WCAG20_Form_HasSubmit",Pass_0:a.Config.helpRoot+"/WCAG20_Form_HasSubmit",Potential_1:a.Config.helpRoot+"/WCAG20_Form_HasSubmit"},RPT_Elem_UniqueId:{0:a.Config.helpRoot+"/RPT_Elem_UniqueId",Pass_0:a.Config.helpRoot+"/RPT_Elem_UniqueId",Fail_1:a.Config.helpRoot+"/RPT_Elem_UniqueId",Fail_2:a.Config.helpRoot+"/XXX"},RPT_Font_ColorInForm:{0:a.Config.helpRoot+"/RPT_Font_ColorInForm",Pass_0:a.Config.helpRoot+"/RPT_Font_ColorInForm",Potential_1:a.Config.helpRoot+"/RPT_Font_ColorInForm"},RPT_Label_UniqueFor:{0:a.Config.helpRoot+"/RPT_Label_UniqueFor",Pass_0:a.Config.helpRoot+"/RPT_Label_UniqueFor",Fail_1:a.Config.helpRoot+"/RPT_Label_UniqueFor"},RPT_Img_AltCommonMisuse:{0:a.Config.helpRoot+"/RPT_Img_AltCommonMisuse",Pass_0:a.Config.helpRoot+"/RPT_Img_AltCommonMisuse",Potential_1:a.Config.helpRoot+"/RPT_Img_AltCommonMisuse"},RPT_Img_LongDescription2:{0:a.Config.helpRoot+"/RPT_Img_LongDescription2",Pass_0:a.Config.helpRoot+"/RPT_Img_LongDescription2",Potential_1:a.Config.helpRoot+"/RPT_Img_LongDescription2"},WCAG20_Img_HasAlt:{0:a.Config.helpRoot+"/WCAG20_Img_HasAlt",Pass_0:a.Config.helpRoot+"/WCAG20_Img_HasAlt",Fail_1:a.Config.helpRoot+"/WCAG20_Img_HasAlt",Fail_2:a.Config.helpRoot+"/WCAG20_Img_HasAlt"},RPT_Style_BackgroundImage:{0:a.Config.helpRoot+"/RPT_Style_BackgroundImage",Pass_0:a.Config.helpRoot+"/RPT_Style_BackgroundImage",Potential_1:a.Config.helpRoot+"/RPT_Style_BackgroundImage"},RPT_Pre_ASCIIArt:{0:a.Config.helpRoot+"/RPT_Pre_ASCIIArt",Pass_0:a.Config.helpRoot+"/RPT_Pre_ASCIIArt",Potential_1:a.Config.helpRoot+"/RPT_Pre_ASCIIArt"},RPT_Media_VideoReferenceTrigger:{0:a.Config.helpRoot+"/RPT_Media_VideoReferenceTrigger",Pass_0:a.Config.helpRoot+"/RPT_Media_VideoReferenceTrigger",Manual_1:a.Config.helpRoot+"/RPT_Media_VideoReferenceTrigger"},RPT_Media_AudioVideoAltFilename:{0:a.Config.helpRoot+"/RPT_Media_AudioVideoAltFilename",Pass_0:a.Config.helpRoot+"/RPT_Media_AudioVideoAltFilename",Potential_1:a.Config.helpRoot+"/RPT_Media_AudioVideoAltFilename"},RPT_Style_ColorSemantics1:{0:a.Config.helpRoot+"/RPT_Style_ColorSemantics1",Pass_0:a.Config.helpRoot+"/RPT_Style_ColorSemantics1",Potential_1:a.Config.helpRoot+"/RPT_Style_ColorSemantics1"},WCAG20_Select_HasOptGroup:{0:a.Config.helpRoot+"/WCAG20_Select_HasOptGroup",Pass_0:a.Config.helpRoot+"/WCAG20_Select_HasOptGroup",Potential_1:a.Config.helpRoot+"/WCAG20_Select_HasOptGroup"},RPT_List_UseMarkup:{0:a.Config.helpRoot+"/RPT_List_UseMarkup",Pass_0:a.Config.helpRoot+"/RPT_List_UseMarkup",Potential_1:a.Config.helpRoot+"/RPT_List_UseMarkup"},RPT_Script_OnclickHTML1:{0:a.Config.helpRoot+"/RPT_Script_OnclickHTML1",Pass_0:a.Config.helpRoot+"/RPT_Script_OnclickHTML1",Potential_1:a.Config.helpRoot+"/RPT_Script_OnclickHTML1"},WCAG20_Table_Structure:{0:a.Config.helpRoot+"/WCAG20_Table_Structure",Pass_0:a.Config.helpRoot+"/WCAG20_Table_Structure",Fail_1:a.Config.helpRoot+"/WCAG20_Table_Structure"},WCAG20_Img_AltTriggerNonDecorative:{0:a.Config.helpRoot+"/WCAG20_Img_AltTriggerNonDecorative",Pass_0:a.Config.helpRoot+"/WCAG20_Img_AltTriggerNonDecorative",Potential_1:a.Config.helpRoot+"/WCAG20_Img_AltTriggerNonDecorative"},WCAG20_Blink_AlwaysTrigger:{0:a.Config.helpRoot+"/WCAG20_Blink_AlwaysTrigger",Pass_0:a.Config.helpRoot+"/WCAG20_Blink_AlwaysTrigger",Fail_1:a.Config.helpRoot+"/WCAG20_Blink_AlwaysTrigger"},RPT_Blink_CSSTrigger1:{0:a.Config.helpRoot+"/RPT_Blink_CSSTrigger1",Pass_0:a.Config.helpRoot+"/RPT_Blink_CSSTrigger1",Potential_1:a.Config.helpRoot+"/RPT_Blink_CSSTrigger1"},RPT_Html_SkipNav:{0:a.Config.helpRoot+"/RPT_Html_SkipNav",Pass_0:a.Config.helpRoot+"/RPT_Html_SkipNav",Potential_1:a.Config.helpRoot+"/RPT_Html_SkipNav"},RPT_Title_Valid:{0:a.Config.helpRoot+"/RPT_Title_Valid",Pass_0:a.Config.helpRoot+"/RPT_Title_Valid",Fail_1:a.Config.helpRoot+"/RPT_Title_Valid",Potential_2:a.Config.helpRoot+"/RPT_Title_Valid"},RPT_Header_HasContent:{0:a.Config.helpRoot+"/RPT_Header_HasContent",Pass_0:a.Config.helpRoot+"/RPT_Header_HasContent",Fail_1:a.Config.helpRoot+"/RPT_Header_HasContent"},WCAG20_Html_HasLang:{0:a.Config.helpRoot+"/WCAG20_Html_HasLang",Pass_0:a.Config.helpRoot+"/WCAG20_Html_HasLang",Fail_1:a.Config.helpRoot+"/WCAG20_Html_HasLang",Fail_2:a.Config.helpRoot+"/WCAG20_Html_HasLang",Fail_3:a.Config.helpRoot+"/WCAG20_Html_HasLang",Fail_4:a.Config.helpRoot+"/WCAG20_Html_HasLang",Potential_5:a.Config.helpRoot+"/WCAG20_Html_HasLang",Potential_6:a.Config.helpRoot+"/WCAG20_Html_HasLang"},WCAG20_Form_TargetAndText:{0:a.Config.helpRoot+"/WCAG20_Form_TargetAndText",Pass_0:a.Config.helpRoot+"/WCAG20_Form_TargetAndText",Potential_1:a.Config.helpRoot+"/WCAG20_Form_TargetAndText"},WCAG20_A_HasText:{0:a.Config.helpRoot+"/WCAG20_A_HasText",Pass_0:a.Config.helpRoot+"/WCAG20_A_HasText",Fail_1:a.Config.helpRoot+"/WCAG20_A_HasText"},WCAG20_Fieldset_HasLegend:{0:a.Config.helpRoot+"/WCAG20_Fieldset_HasLegend",Pass_0:a.Config.helpRoot+"/WCAG20_Fieldset_HasLegend",Fail_1:a.Config.helpRoot+"/WCAG20_Fieldset_HasLegend",Fail_2:a.Config.helpRoot+"/WCAG20_Fieldset_HasLegend",Fail_3:a.Config.helpRoot+"/WCAG20_Fieldset_HasLegend"},RPT_Media_VideoObjectTrigger:{0:a.Config.helpRoot+"/RPT_Media_VideoObjectTrigger",Pass_0:a.Config.helpRoot+"/RPT_Media_VideoObjectTrigger",Manual_1:a.Config.helpRoot+"/RPT_Media_VideoObjectTrigger"},RPT_Text_SensoryReference:{0:a.Config.helpRoot+"/RPT_Text_SensoryReference",Pass_0:a.Config.helpRoot+"/RPT_Text_SensoryReference",Potential_1:a.Config.helpRoot+"/RPT_Text_SensoryReference"},RPT_Embed_AutoStart:{0:a.Config.helpRoot+"/RPT_Embed_AutoStart",Pass_0:a.Config.helpRoot+"/RPT_Embed_AutoStart",Potential_1:a.Config.helpRoot+"/RPT_Embed_AutoStart"},RPT_Style_HinderFocus1:{0:a.Config.helpRoot+"/RPT_Style_HinderFocus1",Pass_0:a.Config.helpRoot+"/RPT_Style_HinderFocus1",Potential_1:a.Config.helpRoot+"/RPT_Style_HinderFocus1"},WCAG20_Elem_Lang_Valid:{0:a.Config.helpRoot+"/WCAG20_Elem_Lang_Valid",Pass_0:a.Config.helpRoot+"/WCAG20_Elem_Lang_Valid",Fail_1:a.Config.helpRoot+"/WCAG20_Elem_Lang_Valid"},WCAG20_Img_LinkTextNotRedundant:{0:a.Config.helpRoot+"/WCAG20_Img_LinkTextNotRedundant",Pass_0:a.Config.helpRoot+"/WCAG20_Img_LinkTextNotRedundant",Fail_1:a.Config.helpRoot+"/WCAG20_Img_LinkTextNotRedundant",Fail_2:a.Config.helpRoot+"/WCAG20_Img_LinkTextNotRedundant",Fail_3:a.Config.helpRoot+"/WCAG20_Img_LinkTextNotRedundant"},RPT_Style_ExternalStyleSheet:{0:a.Config.helpRoot+"/RPT_Style_ExternalStyleSheet",Pass_0:a.Config.helpRoot+"/RPT_Style_ExternalStyleSheet",Potential_1:a.Config.helpRoot+"/RPT_Style_ExternalStyleSheet"},RPT_Header_Trigger:{0:a.Config.helpRoot+"/RPT_Header_Trigger",Pass_0:a.Config.helpRoot+"/RPT_Header_Trigger",Potential_1:a.Config.helpRoot+"/RPT_Header_Trigger"},RPT_Script_OnclickHTML2:{0:a.Config.helpRoot+"/RPT_Script_OnclickHTML2",Pass_0:a.Config.helpRoot+"/RPT_Script_OnclickHTML2",Potential_1:a.Config.helpRoot+"/RPT_Script_OnclickHTML2"},WCAG20_Table_CapSummRedundant:{0:a.Config.helpRoot+"/WCAG20_Table_CapSummRedundant",Pass_0:a.Config.helpRoot+"/WCAG20_Table_CapSummRedundant",Fail_1:a.Config.helpRoot+"/WCAG20_Table_CapSummRedundant"},WCAG20_Input_LabelBefore:{0:a.Config.helpRoot+"/WCAG20_Input_LabelBefore",Pass_0:a.Config.helpRoot+"/WCAG20_Input_LabelBefore",Fail_1:a.Config.helpRoot+"/WCAG20_Input_LabelBefore",Fail_2:a.Config.helpRoot+"/WCAG20_Input_LabelBefore"},WCAG20_Input_LabelAfter:{0:a.Config.helpRoot+"/WCAG20_Input_LabelAfter",Pass_0:a.Config.helpRoot+"/WCAG20_Input_LabelAfter",Fail_1:a.Config.helpRoot+"/WCAG20_Input_LabelAfter",Fail_2:a.Config.helpRoot+"/WCAG20_Input_LabelAfter"},WCAG20_Embed_HasNoEmbed:{0:a.Config.helpRoot+"/WCAG20_Embed_HasNoEmbed",Pass_0:a.Config.helpRoot+"/WCAG20_Embed_HasNoEmbed",Potential_1:a.Config.helpRoot+"/WCAG20_Embed_HasNoEmbed"},WCAG20_Table_Scope_Valid:{0:a.Config.helpRoot+"/WCAG20_Table_Scope_Valid",Pass_0:a.Config.helpRoot+"/WCAG20_Table_Scope_Valid",Fail_1:a.Config.helpRoot+"/WCAG20_Table_Scope_Valid"},WCAG20_Img_TitleEmptyWhenAltNull:{0:a.Config.helpRoot+"/WCAG20_Img_TitleEmptyWhenAltNull",Pass_0:a.Config.helpRoot+"/WCAG20_Img_TitleEmptyWhenAltNull",Fail_1:a.Config.helpRoot+"/WCAG20_Img_TitleEmptyWhenAltNull"},WCAG20_Input_InFieldSet:{0:a.Config.helpRoot+"/WCAG20_Input_InFieldSet",Pass_0:a.Config.helpRoot+"/WCAG20_Input_InFieldSet",Potential_1:a.Config.helpRoot+"/WCAG20_Input_InFieldSet"},WCAG20_Input_RadioChkInFieldSet:{0:a.Config.helpRoot+"/WCAG20_Input_RadioChkInFieldSet",Pass_LoneNogroup:a.Config.helpRoot+"/WCAG20_Input_RadioChkInFieldSet",Pass_Grouped:a.Config.helpRoot+"/WCAG20_Input_RadioChkInFieldSet",Pass_RadioNoName:a.Config.helpRoot+"/WCAG20_Input_RadioChkInFieldSet",Fail_ControlNameMismatch:a.Config.helpRoot+"/WCAG20_Input_RadioChkInFieldSet",Potential_LoneCheckbox:a.Config.helpRoot+"/WCAG20_Input_RadioChkInFieldSet",Potential_UnnamedCheckbox:a.Config.helpRoot+"/WCAG20_Input_RadioChkInFieldSet",Fail_NotGroupedOtherGrouped:a.Config.helpRoot+"/WCAG20_Input_RadioChkInFieldSet",Fail_NotGroupedOtherNotGrouped:a.Config.helpRoot+"/WCAG20_Input_RadioChkInFieldSet",Fail_NotSameGroup:a.Config.helpRoot+"/WCAG20_Input_RadioChkInFieldSet"},WCAG20_Select_NoChangeAction:{0:a.Config.helpRoot+"/WCAG20_Select_NoChangeAction",Pass_0:a.Config.helpRoot+"/WCAG20_Select_NoChangeAction",Potential_1:a.Config.helpRoot+"/WCAG20_Select_NoChangeAction"},WCAG20_Input_HasOnchange:{0:a.Config.helpRoot+"/WCAG20_Input_HasOnchange",Pass_0:a.Config.helpRoot+"/WCAG20_Input_HasOnchange",Potential_1:a.Config.helpRoot+"/WCAG20_Input_HasOnchange"},RPT_Embed_HasAlt:{0:a.Config.helpRoot+"/RPT_Embed_HasAlt",Pass_0:a.Config.helpRoot+"/RPT_Embed_HasAlt",Potential_1:a.Config.helpRoot+"/RPT_Embed_HasAlt"},Valerie_Noembed_HasContent:{0:a.Config.helpRoot+"/Valerie_Noembed_HasContent",Pass_0:a.Config.helpRoot+"/Valerie_Noembed_HasContent",Potential_1:a.Config.helpRoot+"/Valerie_Noembed_HasContent"},Valerie_Caption_HasContent:{0:a.Config.helpRoot+"/Valerie_Caption_HasContent",Pass_0:a.Config.helpRoot+"/Valerie_Caption_HasContent",Fail_1:a.Config.helpRoot+"/Valerie_Caption_HasContent"},Valerie_Caption_InTable:{0:a.Config.helpRoot+"/Valerie_Caption_InTable",Pass_0:a.Config.helpRoot+"/Valerie_Caption_InTable",Fail_1:a.Config.helpRoot+"/Valerie_Caption_InTable"},Valerie_Label_HasContent:{0:a.Config.helpRoot+"/Valerie_Label_HasContent",Pass_Regular:a.Config.helpRoot+"/Valerie_Label_HasContent",Pass_AriaLabel:a.Config.helpRoot+"/Valerie_Label_HasContent",Pass_LabelledBy:a.Config.helpRoot+"/Valerie_Label_HasContent",Fail_1:a.Config.helpRoot+"/Valerie_Label_HasContent"},Valerie_Elem_DirValid:{0:a.Config.helpRoot+"/Valerie_Elem_DirValid",Pass_0:a.Config.helpRoot+"/Valerie_Elem_DirValid",Fail_1:a.Config.helpRoot+"/Valerie_Elem_DirValid"},Valerie_Frame_SrcHtml:{0:a.Config.helpRoot+"/Valerie_Frame_SrcHtml",Pass_0:a.Config.helpRoot+"/Valerie_Frame_SrcHtml",Potential_1:a.Config.helpRoot+"/Valerie_Frame_SrcHtml"},Valerie_Table_DataCellRelationships:{0:a.Config.helpRoot+"/Valerie_Table_DataCellRelationships",Pass_0:a.Config.helpRoot+"/Valerie_Table_DataCellRelationships",Fail_1:a.Config.helpRoot+"/Valerie_Table_DataCellRelationships"},RPT_Table_LayoutTrigger:{0:a.Config.helpRoot+"/RPT_Table_LayoutTrigger",Pass_0:a.Config.helpRoot+"/RPT_Table_LayoutTrigger",Potential_1:a.Config.helpRoot+"/RPT_Table_LayoutTrigger"},RPT_Table_DataHeadingsAria:{0:a.Config.helpRoot+"/RPT_Table_DataHeadingsAria",Pass_0:a.Config.helpRoot+"/RPT_Table_DataHeadingsAria",Fail_1:a.Config.helpRoot+"/RPT_Table_DataHeadingsAria"},WCAG20_Label_RefValid:{0:a.Config.helpRoot+"/WCAG20_Label_RefValid",Pass_0:a.Config.helpRoot+"/WCAG20_Label_RefValid",Fail_1:a.Config.helpRoot+"/WCAG20_Label_RefValid"},WCAG20_Elem_UniqueAccessKey:{0:a.Config.helpRoot+"/WCAG20_Elem_UniqueAccessKey",Pass_0:a.Config.helpRoot+"/WCAG20_Elem_UniqueAccessKey",Fail_1:a.Config.helpRoot+"/WCAG20_Elem_UniqueAccessKey"},WCAG20_Script_FocusBlurs:{0:a.Config.helpRoot+"/WCAG20_Script_FocusBlurs",Pass_0:a.Config.helpRoot+"/WCAG20_Script_FocusBlurs",Potential_1:a.Config.helpRoot+"/WCAG20_Script_FocusBlurs"},HAAC_Img_UsemapAlt:{0:a.Config.helpRoot+"/HAAC_Img_UsemapAlt",Pass_0:a.Config.helpRoot+"/HAAC_Img_UsemapAlt",Fail_1:a.Config.helpRoot+"/HAAC_Img_UsemapAlt"},WCAG20_Text_Emoticons:{0:a.Config.helpRoot+"/WCAG20_Text_Emoticons",Pass_0:a.Config.helpRoot+"/WCAG20_Text_Emoticons",Potential_1:a.Config.helpRoot+"/WCAG20_Text_Emoticons"},WCAG20_Style_BeforeAfter:{0:a.Config.helpRoot+"/WCAG20_Style_BeforeAfter",Pass_0:a.Config.helpRoot+"/WCAG20_Style_BeforeAfter",Potential_1:a.Config.helpRoot+"/WCAG20_Style_BeforeAfter"},WCAG20_Text_LetterSpacing:{0:a.Config.helpRoot+"/WCAG20_Text_LetterSpacing",Pass_0:a.Config.helpRoot+"/WCAG20_Text_LetterSpacing",Potential_1:a.Config.helpRoot+"/WCAG20_Text_LetterSpacing"},Rpt_Aria_ValidRole:{0:a.Config.helpRoot+"/Rpt_Aria_ValidRole",Pass_0:a.Config.helpRoot+"/Rpt_Aria_ValidRole",Fail_1:a.Config.helpRoot+"/Rpt_Aria_ValidRole"},Rpt_Aria_ValidPropertyValue:{0:a.Config.helpRoot+"/Rpt_Aria_ValidPropertyValue",Pass_0:a.Config.helpRoot+"/Rpt_Aria_ValidPropertyValue",Fail_1:a.Config.helpRoot+"/Rpt_Aria_ValidPropertyValue"},Rpt_Aria_ValidIdRef:{0:a.Config.helpRoot+"/Rpt_Aria_ValidIdRef",Pass_0:a.Config.helpRoot+"/Rpt_Aria_ValidIdRef",Fail_1:a.Config.helpRoot+"/Rpt_Aria_ValidIdRef"},Rpt_Aria_RequiredProperties:{0:a.Config.helpRoot+"/Rpt_Aria_RequiredProperties",Pass_0:a.Config.helpRoot+"/Rpt_Aria_RequiredProperties",Fail_1:a.Config.helpRoot+"/Rpt_Aria_RequiredProperties"},Rpt_Aria_EmptyPropertyValue:{0:a.Config.helpRoot+"/Rpt_Aria_EmptyPropertyValue",Pass_0:a.Config.helpRoot+"/Rpt_Aria_EmptyPropertyValue",Fail_1:a.Config.helpRoot+"/Rpt_Aria_EmptyPropertyValue"},Rpt_Aria_ValidProperty:{0:a.Config.helpRoot+"/Rpt_Aria_ValidProperty",Pass_0:a.Config.helpRoot+"/Rpt_Aria_ValidProperty",Fail_1:a.Config.helpRoot+"/Rpt_Aria_ValidProperty"},Rpt_Aria_InvalidTabindexForActivedescendant:{0:a.Config.helpRoot+"/Rpt_Aria_InvalidTabindexForActivedescendant",Pass_0:a.Config.helpRoot+"/Rpt_Aria_InvalidTabindexForActivedescendant",Fail_1:a.Config.helpRoot+"/Rpt_Aria_InvalidTabindexForActivedescendant"},Rpt_Aria_MissingFocusableChild:{0:a.Config.helpRoot+"/Rpt_Aria_MissingFocusableChild",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MissingFocusableChild",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MissingFocusableChild"},Rpt_Aria_MissingKeyboardHandler:{0:a.Config.helpRoot+"/Rpt_Aria_MissingKeyboardHandler",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MissingKeyboardHandler",Potential_1:a.Config.helpRoot+"/Rpt_Aria_MissingKeyboardHandler"},WCAG20_Img_PresentationImgHasNonNullAlt:{0:a.Config.helpRoot+"/WCAG20_Img_PresentationImgHasNonNullAlt",Pass_0:a.Config.helpRoot+"/WCAG20_Img_PresentationImgHasNonNullAlt",Fail_1:a.Config.helpRoot+"/WCAG20_Img_PresentationImgHasNonNullAlt"},Rpt_Aria_MultipleSearchLandmarks:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleSearchLandmarks",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleSearchLandmarks",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleSearchLandmarks"},Rpt_Aria_MultipleApplicationLandmarks:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleApplicationLandmarks",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleApplicationLandmarks",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleApplicationLandmarks"},Rpt_Aria_ApplicationLandmarkLabel:{0:a.Config.helpRoot+"/Rpt_Aria_ApplicationLandmarkLabel",Pass_0:a.Config.helpRoot+"/Rpt_Aria_ApplicationLandmarkLabel",Fail_1:a.Config.helpRoot+"/Rpt_Aria_ApplicationLandmarkLabel"},Rpt_Aria_MultipleDocumentRoles:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleDocumentRoles",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleDocumentRoles",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleDocumentRoles"},WCAG20_Label_TargetInvisible:{0:a.Config.helpRoot+"/WCAG20_Label_TargetInvisible",Pass_0:a.Config.helpRoot+"/WCAG20_Label_TargetInvisible",Potential_1:a.Config.helpRoot+"/WCAG20_Label_TargetInvisible"},HAAC_Video_HasNoTrack:{0:a.Config.helpRoot+"/HAAC_Video_HasNoTrack",Pass_0:a.Config.helpRoot+"/HAAC_Video_HasNoTrack",Potential_1:a.Config.helpRoot+"/HAAC_Video_HasNoTrack"},HAAC_Audio_Video_Trigger:{0:a.Config.helpRoot+"/HAAC_Audio_Video_Trigger",Pass_0:a.Config.helpRoot+"/HAAC_Audio_Video_Trigger",Manual_1:a.Config.helpRoot+"/HAAC_Audio_Video_Trigger"},HAAC_Input_HasRequired:{0:a.Config.helpRoot+"/HAAC_Input_HasRequired",Pass_0:a.Config.helpRoot+"/HAAC_Input_HasRequired",Potential_1:a.Config.helpRoot+"/HAAC_Input_HasRequired"},HAAC_Aria_ImgAlt:{0:a.Config.helpRoot+"/HAAC_Aria_ImgAlt",Pass_0:a.Config.helpRoot+"/HAAC_Aria_ImgAlt",Fail_1:a.Config.helpRoot+"/HAAC_Aria_ImgAlt",Fail_2:a.Config.helpRoot+"/HAAC_Aria_ImgAlt",Fail_3:a.Config.helpRoot+"/HAAC_Aria_ImgAlt"},HAAC_BackgroundImg_HasTextOrTitle:{0:a.Config.helpRoot+"/HAAC_BackgroundImg_HasTextOrTitle",Pass_0:a.Config.helpRoot+"/HAAC_BackgroundImg_HasTextOrTitle",Manual_1:a.Config.helpRoot+"/HAAC_BackgroundImg_HasTextOrTitle"},HAAC_Accesskey_NeedLabel:{0:a.Config.helpRoot+"/HAAC_Accesskey_NeedLabel",Pass_0:a.Config.helpRoot+"/HAAC_Accesskey_NeedLabel",Potential_1:a.Config.helpRoot+"/HAAC_Accesskey_NeedLabel"},HAAC_Aria_Or_HTML5_Attr:{0:a.Config.helpRoot+"/HAAC_Aria_Or_HTML5_Attr",Pass_0:a.Config.helpRoot+"/HAAC_Aria_Or_HTML5_Attr",Fail_1:a.Config.helpRoot+"/HAAC_Aria_Or_HTML5_Attr"},HAAC_Canvas:{0:a.Config.helpRoot+"/HAAC_Canvas",Pass_0:a.Config.helpRoot+"/HAAC_Canvas",Manual_1:a.Config.helpRoot+"/HAAC_Canvas"},HAAC_Figure_label:{0:a.Config.helpRoot+"/HAAC_Figure_label",Pass_0:a.Config.helpRoot+"/HAAC_Figure_label",Fail_1:a.Config.helpRoot+"/HAAC_Figure_label"},HAAC_Input_Placeholder:{0:a.Config.helpRoot+"/HAAC_Input_Placeholder",Pass_0:a.Config.helpRoot+"/HAAC_Input_Placeholder",Potential_1:a.Config.helpRoot+"/HAAC_Input_Placeholder",Potential_2:a.Config.helpRoot+"/HAAC_Input_Placeholder"},HAAC_Aria_Native_Host_Sematics:{0:a.Config.helpRoot+"/HAAC_Aria_Native_Host_Sematics",Pass_0:a.Config.helpRoot+"/HAAC_Aria_Native_Host_Sematics",Fail_1:a.Config.helpRoot+"/HAAC_Aria_Native_Host_Sematics"},RPT_Form_ChangeEmpty:{0:a.Config.helpRoot+"/RPT_Form_ChangeEmpty",Pass_0:a.Config.helpRoot+"/RPT_Form_ChangeEmpty",Potential_1:a.Config.helpRoot+"/RPT_Form_ChangeEmpty"},IBMA_Color_Contrast_WCAG2AA:{0:a.Config.helpRoot+"/IBMA_Color_Contrast_WCAG2AA",Pass_0:a.Config.helpRoot+"/IBMA_Color_Contrast_WCAG2AA",Fail_1:a.Config.helpRoot+"/IBMA_Color_Contrast_WCAG2AA"},IBMA_Color_Contrast_WCAG2AA_PV:{0:a.Config.helpRoot+"/IBMA_Color_Contrast_WCAG2AA_PV",Pass_0:a.Config.helpRoot+"/IBMA_Color_Contrast_WCAG2AA_PV",Potential_1:a.Config.helpRoot+"/IBMA_Color_Contrast_WCAG2AA_PV"},WCAG20_Body_FirstASkips_Native_Host_Sematics:{0:a.Config.helpRoot+"/WCAG20_Body_FirstASkips_Native_Host_Sematics",Pass_0:a.Config.helpRoot+"/WCAG20_Body_FirstASkips_Native_Host_Sematics",Fail_1:a.Config.helpRoot+"/WCAG20_Body_FirstASkips_Native_Host_Sematics"},WCAG20_Body_FirstAContainsSkipText_Native_Host_Sematics:{0:a.Config.helpRoot+"/WCAG20_Body_FirstAContainsSkipText_Native_Host_Sematics",Pass_0:a.Config.helpRoot+"/WCAG20_Body_FirstAContainsSkipText_Native_Host_Sematics",Potential_1:a.Config.helpRoot+"/WCAG20_Body_FirstAContainsSkipText_Native_Host_Sematics"},Rpt_Aria_RequiredChildren_Native_Host_Sematics:{0:a.Config.helpRoot+"/Rpt_Aria_RequiredChildren_Native_Host_Sematics",Pass_0:a.Config.helpRoot+"/Rpt_Aria_RequiredChildren_Native_Host_Sematics",Potential_1:a.Config.helpRoot+"/Rpt_Aria_RequiredChildren_Native_Host_Sematics"},Rpt_Aria_RequiredParent_Native_Host_Sematics:{0:a.Config.helpRoot+"/Rpt_Aria_RequiredParent_Native_Host_Sematics",Pass_0:a.Config.helpRoot+"/Rpt_Aria_RequiredParent_Native_Host_Sematics",Fail_1:a.Config.helpRoot+"/Rpt_Aria_RequiredParent_Native_Host_Sematics"},Rpt_Aria_EventHandlerMissingRole_Native_Host_Sematics:{0:a.Config.helpRoot+"/Rpt_Aria_EventHandlerMissingRole_Native_Host_Sematics",Pass_0:a.Config.helpRoot+"/Rpt_Aria_EventHandlerMissingRole_Native_Host_Sematics",Fail_1:a.Config.helpRoot+"/Rpt_Aria_EventHandlerMissingRole_Native_Host_Sematics"},Rpt_Aria_WidgetLabels_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_WidgetLabels_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_WidgetLabels_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_WidgetLabels_Implicit"},Rpt_Aria_OrphanedContent_Native_Host_Sematics:{0:a.Config.helpRoot+"/Rpt_Aria_OrphanedContent_Native_Host_Sematics",Pass_0:a.Config.helpRoot+"/Rpt_Aria_OrphanedContent_Native_Host_Sematics",Fail_1:a.Config.helpRoot+"/Rpt_Aria_OrphanedContent_Native_Host_Sematics"},Rpt_Aria_RegionLabel_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_RegionLabel_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_RegionLabel_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_RegionLabel_Implicit",Fail_2:a.Config.helpRoot+"/Rpt_Aria_RegionLabel_Implicit"},Rpt_Aria_MultipleMainsVisibleLabel_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleMainsVisibleLabel_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleMainsVisibleLabel_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleMainsVisibleLabel_Implicit"},Rpt_Aria_MultipleBannerLandmarks_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleBannerLandmarks_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleBannerLandmarks_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleBannerLandmarks_Implicit"},Rpt_Aria_MultipleComplementaryLandmarks_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleComplementaryLandmarks_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleComplementaryLandmarks_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleComplementaryLandmarks_Implicit"},Rpt_Aria_MultipleContentinfoLandmarks_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleContentinfoLandmarks_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleContentinfoLandmarks_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleContentinfoLandmarks_Implicit"},Rpt_Aria_MultipleFormLandmarks_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleFormLandmarks_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleFormLandmarks_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleFormLandmarks_Implicit"},Rpt_Aria_MultipleNavigationLandmarks_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleNavigationLandmarks_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleNavigationLandmarks_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleNavigationLandmarks_Implicit"},Rpt_Aria_ComplementaryLandmarkLabel_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_ComplementaryLandmarkLabel_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_ComplementaryLandmarkLabel_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_ComplementaryLandmarkLabel_Implicit"},Rpt_Aria_MultipleArticleRoles_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleArticleRoles_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleArticleRoles_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleArticleRoles_Implicit"},Rpt_Aria_ArticleRoleLabel_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_ArticleRoleLabel_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_ArticleRoleLabel_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_ArticleRoleLabel_Implicit"},Rpt_Aria_MultipleGroupRoles_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleGroupRoles_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleGroupRoles_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleGroupRoles_Implicit"},Rpt_Aria_GroupRoleLabel_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_GroupRoleLabel_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_GroupRoleLabel_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_GroupRoleLabel_Implicit"},Rpt_Aria_MultipleContentinfoInSiblingSet_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleContentinfoInSiblingSet_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleContentinfoInSiblingSet_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleContentinfoInSiblingSet_Implicit"},Rpt_Aria_OneBannerInSiblingSet_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_OneBannerInSiblingSet_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_OneBannerInSiblingSet_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_OneBannerInSiblingSet_Implicit"},Rpt_Aria_ContentinfoWithNoMain_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_ContentinfoWithNoMain_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_ContentinfoWithNoMain_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_ContentinfoWithNoMain_Implicit"},Rpt_Aria_ComplementaryRequiredLabel_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_ComplementaryRequiredLabel_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_ComplementaryRequiredLabel_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_ComplementaryRequiredLabel_Implicit"},Rpt_Aria_MultipleRegionsUniqueLabel_Implicit:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleRegionsUniqueLabel_Implicit",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleRegionsUniqueLabel_Implicit",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleRegionsUniqueLabel_Implicit"},IBMA_Focus_Tabbable:{0:a.Config.helpRoot+"/IBMA_Focus_Tabbable",Pass_0:a.Config.helpRoot+"/IBMA_Focus_Tabbable",Potential_1:a.Config.helpRoot+"/IBMA_Focus_Tabbable"},IBMA_Focus_MultiTab:{0:a.Config.helpRoot+"/IBMA_Focus_MultiTab",Pass_0:a.Config.helpRoot+"/IBMA_Focus_MultiTab",Potential_1:a.Config.helpRoot+"/IBMA_Focus_MultiTab"},WCAG20_Table_SummaryAria3:{0:a.Config.helpRoot+"/WCAG20_Table_SummaryAria3",Pass_0:a.Config.helpRoot+"/WCAG20_Table_SummaryAria3",Potential_1:a.Config.helpRoot+"/WCAG20_Table_SummaryAria3"},RPT_Style_Trigger2:{0:a.Config.helpRoot+"/RPT_Style_Trigger2",Pass_0:a.Config.helpRoot+"/RPT_Style_Trigger2",Manual_1:a.Config.helpRoot+"/RPT_Style_Trigger2"},Rpt_Aria_MultipleMainsRequireLabel_Implicit_2:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleMainsRequireLabel_Implicit_2",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleMainsRequireLabel_Implicit_2",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleMainsRequireLabel_Implicit_2"},HAAC_Media_DocumentTrigger2:{0:a.Config.helpRoot+"/HAAC_Media_DocumentTrigger2",Pass_0:a.Config.helpRoot+"/HAAC_Media_DocumentTrigger2",Manual_1:a.Config.helpRoot+"/HAAC_Media_DocumentTrigger2"},HAAC_Aria_ErrorMessage:{0:a.Config.helpRoot+"/HAAC_Aria_ErrorMessage",Pass_0:a.Config.helpRoot+"/HAAC_Aria_ErrorMessage",Fail_1:a.Config.helpRoot+"/HAAC_Aria_ErrorMessage",Fail_2:a.Config.helpRoot+"/HAAC_Aria_ErrorMessage"},HAAC_List_Group_ListItem:{0:a.Config.helpRoot+"/HAAC_List_Group_ListItem",Pass_0:a.Config.helpRoot+"/HAAC_List_Group_ListItem",Fail_1:a.Config.helpRoot+"/HAAC_List_Group_ListItem"},HAAC_ActiveDescendantCheck:{0:a.Config.helpRoot+"/HAAC_ActiveDescendantCheck",Pass_0:a.Config.helpRoot+"/HAAC_ActiveDescendantCheck",Fail_1:a.Config.helpRoot+"/HAAC_ActiveDescendantCheck",Fail_2:a.Config.helpRoot+"/HAAC_ActiveDescendantCheck",Fail_3:a.Config.helpRoot+"/HAAC_ActiveDescendantCheck",Fail_4:a.Config.helpRoot+"/HAAC_ActiveDescendantCheck"},HAAC_Application_Role_Text:{0:a.Config.helpRoot+"/HAAC_Application_Role_Text",Pass_0:a.Config.helpRoot+"/HAAC_Application_Role_Text",Potential_1:a.Config.helpRoot+"/HAAC_Application_Role_Text"},Rpt_Aria_MultipleToolbarUniqueLabel:{0:a.Config.helpRoot+"/Rpt_Aria_MultipleToolbarUniqueLabel",Pass_0:a.Config.helpRoot+"/Rpt_Aria_MultipleToolbarUniqueLabel",Fail_1:a.Config.helpRoot+"/Rpt_Aria_MultipleToolbarUniqueLabel"},HAAC_Combobox_ARIA_11_Guideline:{0:a.Config.helpRoot+"/HAAC_Combobox_ARIA_11_Guideline",Pass_0:a.Config.helpRoot+"/HAAC_Combobox_ARIA_11_Guideline",Manual_1:a.Config.helpRoot+"/HAAC_Combobox_ARIA_11_Guideline"},HAAC_Combobox_Must_Have_Text_Input:{0:a.Config.helpRoot+"/HAAC_Combobox_Must_Have_Text_Input",Pass_0:a.Config.helpRoot+"/HAAC_Combobox_Must_Have_Text_Input",Fail_1:a.Config.helpRoot+"/HAAC_Combobox_Must_Have_Text_Input"},HAAC_Combobox_DOM_Focus:{0:a.Config.helpRoot+"/HAAC_Combobox_DOM_Focus",Pass_0:a.Config.helpRoot+"/HAAC_Combobox_DOM_Focus",Fail_1:a.Config.helpRoot+"/HAAC_Combobox_DOM_Focus"},HAAC_Combobox_Autocomplete:{0:a.Config.helpRoot+"/HAAC_Combobox_Autocomplete",Pass_0:a.Config.helpRoot+"/HAAC_Combobox_Autocomplete",Fail_1:a.Config.helpRoot+"/HAAC_Combobox_Autocomplete"},HAAC_Combobox_Autocomplete_Invalid:{0:a.Config.helpRoot+"/HAAC_Combobox_Autocomplete_Invalid",Pass_0:a.Config.helpRoot+"/HAAC_Combobox_Autocomplete_Invalid",Fail_1:a.Config.helpRoot+"/HAAC_Combobox_Autocomplete_Invalid"},HAAC_Combobox_Expanded:{0:a.Config.helpRoot+"/HAAC_Combobox_Expanded",Pass_0:a.Config.helpRoot+"/HAAC_Combobox_Expanded",Fail_1:a.Config.helpRoot+"/HAAC_Combobox_Expanded"},HAAC_Combobox_Popup:{0:a.Config.helpRoot+"/HAAC_Combobox_Popup",Pass_0:a.Config.helpRoot+"/HAAC_Combobox_Popup",Fail_1:a.Config.helpRoot+"/HAAC_Combobox_Popup"},WCAG21_Style_Viewport:{0:a.Config.helpRoot+"/WCAG21_Style_Viewport",Pass_0:a.Config.helpRoot+"/WCAG21_Style_Viewport",Potential_1:a.Config.helpRoot+"/WCAG21_Style_Viewport"},WCAG21_Label_Accessible:{0:a.Config.helpRoot+"/WCAG21_Label_Accessible",Pass_0:a.Config.helpRoot+"/WCAG21_Label_Accessible",Fail_1:a.Config.helpRoot+"/WCAG21_Label_Accessible"},WCAG21_Input_Autocomplete:{0:a.Config.helpRoot+"/WCAG21_Input_Autocomplete",Pass_0:a.Config.helpRoot+"/WCAG21_Input_Autocomplete",Fail_1:a.Config.helpRoot+"/WCAG21_Input_Autocomplete"},WCAG20_Input_VisibleLabel:{0:a.Config.helpRoot+"/WCAG20_Input_VisibleLabel",Pass_0:a.Config.helpRoot+"/WCAG20_Input_VisibleLabel",Potential_1:a.Config.helpRoot+"/WCAG20_Input_VisibleLabel"}};t.a11yHelp=l},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.checkRulesets=void 0;var a=i(67),l=i(68),o=[];t.checkRulesets=o,t.checkRulesets=o=o.concat(a.a11yRulesets),t.checkRulesets=o=o.concat(l.designRulesets)},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.a11yRulesets=void 0;var a=i(0),l=[{id:"IBM_Accessibility",name:"IBM Accessibility",category:a.eRuleCategory.ACCESSIBILITY,description:"Rules for WCAG 2.1 AA plus additional IBM checklist supplemental requirements.",checkpoints:[{num:"1.1.1",name:"Non-text Content",summary:"All non-text content that is presented to the user has a text alternative that serves the equivalent purpose.",rules:[{id:"WCAG20_Input_ExplicitLabelImage",level:a.eRulePolicy.VIOLATION},{id:"RPT_Img_UsemapValid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Object_HasText",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Applet_HasAlt",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_ExplicitLabel",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Area_HasAlt",level:a.eRulePolicy.VIOLATION},{id:"RPT_Img_AltCommonMisuse",level:a.eRulePolicy.VIOLATION},{id:"RPT_Img_LongDescription2",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_HasAlt",level:a.eRulePolicy.VIOLATION},{id:"RPT_Pre_ASCIIArt",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_AudioVideoAltFilename",level:a.eRulePolicy.VIOLATION},{id:"RPT_Style_BackgroundImage",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_LinkTextNotRedundant",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_TitleEmptyWhenAltNull",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Img_UsemapAlt",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Text_Emoticons",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_PresentationImgHasNonNullAlt",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Figure_label",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_AltBrief",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Embed_HasNoEmbed",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Embed_HasAlt",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Style_Trigger2",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_BackgroundImg_HasTextOrTitle",level:a.eRulePolicy.RECOMMENDATION},{id:"Valerie_Noembed_HasContent",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_Canvas",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.1",name:"Audio-only and Video-only (Prerecorded)",summary:"For prerecorded audio-only or video-only media, an alternative provides equivalent information.",rules:[{id:"HAAC_Video_HasNoTrack",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_AudioTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.2",name:"Captions (Prerecorded)",summary:"Captions are provided for all prerecorded audio content in synchronized media.",rules:[{id:"HAAC_Video_HasNoTrack",level:a.eRulePolicy.VIOLATION}]},{num:"1.2.3",name:"Audio Description or Media Alternative (Prerecorded)",summary:"An alternative for time-based media or audio description of the prerecorded video content is provided for synchronized media.",rules:[{id:"RPT_Media_VideoReferenceTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.4",name:"Captions (Live)",summary:"Captions are provided for all live audio content in synchronized media.",rules:[{id:"HAAC_Video_HasNoTrack",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_VideoObjectTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.5",name:"Audio Description (Prerecorded)",summary:"Audio description is provided for all prerecorded video content in synchronized media.",rules:[{id:"RPT_Media_VideoReferenceTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.3.1",name:"Info and Relationships",summary:"Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.",rules:[{id:"RPT_Headers_FewWords",level:a.eRulePolicy.VIOLATION},{id:"RPT_Blockquote_HasCite",level:a.eRulePolicy.VIOLATION},{id:"RPT_Blockquote_WrapsTextQuote",level:a.eRulePolicy.VIOLATION},{id:"RPT_Block_ShouldBeHeading",level:a.eRulePolicy.VIOLATION},{id:"RPT_Label_UniqueFor",level:a.eRulePolicy.VIOLATION},{id:"RPT_List_UseMarkup",level:a.eRulePolicy.VIOLATION},{id:"RPT_Script_OnclickHTML1",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Fieldset_HasLegend",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Table_CapSummRedundant",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Table_Scope_Valid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_RadioChkInFieldSet",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Caption_HasContent",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Caption_InTable",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Table_DataCellRelationships",level:a.eRulePolicy.VIOLATION},{id:"RPT_Table_DataHeadingsAria",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Label_RefValid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Style_BeforeAfter",level:a.eRulePolicy.VIOLATION},{id:"RPT_List_Misuse",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Select_HasOptGroup",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Script_OnclickHTML2",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Input_InFieldSet",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Table_LayoutTrigger",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Table_Structure",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Table_SummaryAria3",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.3.2",name:"Meaningful Sequence",summary:"When the sequence in which content is presented affects its meaning, a correct reading sequence can be programmatically determined.",rules:[{id:"Valerie_Elem_DirValid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Text_LetterSpacing",level:a.eRulePolicy.VIOLATION}]},{num:"1.3.3",name:"Sensory Characteristics",summary:"Instructions provided for understanding and operating content do not rely solely on sensory characteristics of components such as shape, size, visual location, orientation, or sound.",rules:[{id:"RPT_Text_SensoryReference",level:a.eRulePolicy.VIOLATION}]},{num:"1.3.4",name:"Orientation",summary:"Content does not restrict its view and operation to a single display orientation, such as portrait or landscape.",rules:[]},{num:"1.3.5",name:"Identify Input Purpose",summary:"The purpose of each input field that collects information about the user can be programmatically determined when the field serves a common purpose.",rules:[{id:"WCAG21_Input_Autocomplete",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.1",name:"Use of Color",summary:"Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.",rules:[{id:"RPT_Font_ColorInForm",level:a.eRulePolicy.VIOLATION},{id:"RPT_Style_ColorSemantics1",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.2",name:"Audio Control",summary:"If any audio plays automatically for more than 3 seconds, either a mechanism is available to pause or stop the audio, or a mechanism is available to control audio volume independently from the overall system volume level.",rules:[{id:"RPT_Embed_AutoStart",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.3",name:"Contrast (Minimum)",summary:"The visual presentation of text and images of text has a contrast ratio of at least 4.5:1, with a 3:1 ratio for large-scale text.",rules:[{id:"IBMA_Color_Contrast_WCAG2AA",level:a.eRulePolicy.VIOLATION},{id:"IBMA_Color_Contrast_WCAG2AA_PV",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.4",name:"Resize Text",summary:"Text can be resized without assistive technology up to 200 percent without loss of content or functionality.",rules:[{id:"WCAG21_Style_Viewport",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.5",name:"Images of Text",summary:"If the technologies being used can achieve the visual presentation, text is used to convey information rather than images of text.",rules:[]},{num:"1.4.10",name:"Reflow",summary:"Content can reflow without loss of information or functionality, and without requiring scrolling in two dimensions.",rules:[]},{num:"1.4.11",name:"Non-text Contrast",summary:"The parts of graphical objects required to understand the content, and the visual information required to identify UI components and states, have a contrast ratio of at least 3:1 against adjacent colors.",rules:[]},{num:"1.4.12",name:"Text Spacing",summary:"No loss of content or functionality occurs when users change letter, word and paragraph spacing, as well as line height.",rules:[]},{num:"1.4.13",name:"Content on Hover or Focus",summary:"Where hover or focus actions cause additional content to become visible and hidden, the additional content is dismissable, hoverable and persistent.",rules:[]},{num:"2.1.1",name:"Keyboard",summary:"All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes.",rules:[{id:"RPT_Elem_EventMouseAndKey",level:a.eRulePolicy.RECOMMENDATION},{id:"Rpt_Aria_InvalidTabindexForActivedescendant",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MissingFocusableChild",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MissingKeyboardHandler",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Audio_Video_Trigger",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Application_Role_Text",level:a.eRulePolicy.VIOLATION}]},{num:"2.1.2",name:"No Keyboard Trap",summary:"If keyboard focus can be moved to a component using a keyboard interface, then focus can be moved away from that component using only a keyboard interface, and, if it requires more than unmodified arrow or tab keys or other standard exit methods, the user is advised of the method for moving focus away.",rules:[{id:"HAAC_Media_DocumentTrigger2",level:a.eRulePolicy.RECOMMENDATION}]},{num:"2.1.4",name:"Character Key Shortcuts",summary:"If a keyboard shortcut is implemented using only letter, punctuation, number or symbol characters, then the shortcut can be turned off, remapped or activated only on focus.",rules:[]},{num:"2.2.1",name:"Timing Adjustable",summary:"For each time limit that is set by the content, the user can turn off, adjust, or extend the limit.",rules:[{id:"RPT_Meta_Refresh",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Meta_RedirectZero",level:a.eRulePolicy.VIOLATION}]},{num:"2.2.2",name:"Pause, Stop, Hide",summary:"For moving, blinking, scrolling, or auto-updating information, the user can pause, stop, hide or adjust the information.",rules:[{id:"RPT_Marquee_Trigger",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Blink_AlwaysTrigger",level:a.eRulePolicy.VIOLATION},{id:"RPT_Blink_CSSTrigger1",level:a.eRulePolicy.VIOLATION}]},{num:"2.3.1",name:"Three Flashes or Below Threshold",summary:"Content does not contain anything that flashes more than three times in any one second period, or the flash is below the general flash and red flash thresholds.",rules:[]},{num:"2.4.1",name:"Bypass Blocks",summary:"A mechanism is available to bypass blocks of content that are repeated on multiple Web pages.",rules:[{id:"WCAG20_Frame_HasTitle",level:a.eRulePolicy.VIOLATION},{id:"RPT_Html_SkipNav",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Frame_SrcHtml",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleSearchLandmarks",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ComplementaryLandmarkLabel_Implicit",level:a.eRulePolicy.RECOMMENDATION},{id:"Rpt_Aria_MultipleApplicationLandmarks",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ApplicationLandmarkLabel",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleDocumentRoles",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Body_FirstASkips_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Body_FirstAContainsSkipText_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_OrphanedContent_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_RegionLabel_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleMainsVisibleLabel_Implicit",level:a.eRulePolicy.RECOMMENDATION},{id:"Rpt_Aria_MultipleBannerLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleComplementaryLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleContentinfoLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleFormLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleNavigationLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleArticleRoles_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleGroupRoles_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleContentinfoInSiblingSet_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_OneBannerInSiblingSet_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ContentinfoWithNoMain_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ComplementaryRequiredLabel_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleRegionsUniqueLabel_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleMainsRequireLabel_Implicit_2",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.2",name:"Page Titled",summary:"Web pages, non-web documents, and software have titles that describe topic or purpose.",rules:[{id:"WCAG20_Doc_HasTitle",level:a.eRulePolicy.VIOLATION},{id:"RPT_Title_Valid",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.3",name:"Focus Order",summary:"If content can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components receive focus in an order that preserves meaning and operability.",rules:[]},{num:"2.4.4",name:"Link Purpose (In Context)",summary:"The purpose of each link can be determined from the link text alone or from the link text together with its programmatically determined link content.",rules:[{id:"WCAG20_A_HasText",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.5",name:"Multiple Ways",summary:"More than one way is available to locate a Web page within a set of Web pages, except where the Web Page is the result of, or a step in, a process.",rules:[]},{num:"2.4.6",name:"Headings and Labels",summary:"Headings and labels describe topic or purpose.",rules:[{id:"RPT_Header_HasContent",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.7",name:"Focus Visible",summary:"Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible.",rules:[{id:"RPT_Style_HinderFocus1",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Script_FocusBlurs",level:a.eRulePolicy.VIOLATION}]},{num:"2.5.1",name:"Pointer Gestures",summary:"All functionality that uses multipoint or path-based gestures for operation can be operated with a single pointer without a path-based gesture.",rules:[]},{num:"2.5.2",name:"Pointer Cancellation",summary:"For functionality that can be operated using a single pointer, completion of the function is on the up-event with an ability to abort, undo or reverse the outcome.",rules:[]},{num:"2.5.3",name:"Label in Name",summary:"For user interface components with labels that include text or images of text, the accessible name contains the text that is presented visually.",rules:[{id:"WCAG21_Label_Accessible",level:a.eRulePolicy.VIOLATION}]},{num:"2.5.4",name:"Motion Actuation",summary:"Functionality that can be operated by motion can also be operated by user interface components, and the motion trigger can be disabled.",rules:[]},{num:"3.1.1",name:"Language of Page",summary:"The default human language of Web pages, non-Web documents, or software can be programmatically determined.",rules:[{id:"WCAG20_Html_HasLang",level:a.eRulePolicy.VIOLATION}]},{num:"3.1.2",name:"Language of Parts",summary:"The human language of each passage or phrase in the content can be programmatically determined.",rules:[{id:"WCAG20_Elem_Lang_Valid",level:a.eRulePolicy.VIOLATION}]},{num:"3.2.1",name:"On Focus",summary:"When any component receives focus, it does not initiate a change of context.",rules:[{id:"WCAG20_Select_NoChangeAction",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Script_FocusBlurs",level:a.eRulePolicy.VIOLATION}]},{num:"3.2.2",name:"On Input",summary:"Changing the setting of any user interface component does not automatically cause a change of context unless the user has been advised of the behavior before using the component.",rules:[{id:"WCAG20_A_TargetAndText",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Form_HasSubmit",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Form_TargetAndText",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_HasOnchange",level:a.eRulePolicy.VIOLATION},{id:"RPT_Form_ChangeEmpty",level:a.eRulePolicy.RECOMMENDATION}]},{num:"3.2.3",name:"Consistent Navigation",summary:"Navigational mechanisms that are repeated on multiple Web pages within a set of Web pages occur in the same relative order each time they are repeated, unless a change is initiated by the user.",rules:[]},{num:"3.2.4",name:"Consistent Identification",summary:"Components that have the same functionality within a set of Web pages are identified consistently.",rules:[]},{num:"3.3.1",name:"Error Identification",summary:"If an input error is automatically detected, the item that is in error is identified and the error is described to the user in text.",rules:[{id:"HAAC_Aria_ErrorMessage",level:a.eRulePolicy.VIOLATION}]},{num:"3.3.2",name:"Labels or Instructions",summary:"Labels or instructions are provided when content requires user input.",rules:[{id:"WCAG20_Input_LabelBefore",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_LabelAfter",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Accesskey_NeedLabel",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_Aria_Or_HTML5_Attr",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Input_Placeholder",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_VisibleLabel",level:a.eRulePolicy.VIOLATION}]},{num:"3.3.3",name:"Error Suggestion",summary:"If an input error is automatically detected and suggestions for correction are known, then the suggestions are provided to the user, unless it would jeopardize the security or purpose of the content.",rules:[]},{num:"3.3.4",name:"Error Prevention (Legal, Financial, Data)",summary:"For content that cause legal commitments or financial transactions for the user to occur, that modify or delete user-controllable data in data storage systems, or that submit user test responses, the user can reverse, correct, or confirm the action.",rules:[]},{num:"4.1.1",name:"Parsing",summary:"In content implemented using markup languages, elements have complete start and end tags, elements are nested according to their specifications, elements do not contain duplicate attributes, and any IDs are unique, except where the specifications allow these features.",rules:[{id:"RPT_Elem_UniqueId",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Elem_UniqueAccessKey",level:a.eRulePolicy.VIOLATION}]},{num:"4.1.2",name:"Name, Role, Value",summary:"For all user interface components (including, but not limited to: form elements, links and components generated by scripts), the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to user agents, including assistive technologies.",rules:[{id:"WCAG20_Input_ExplicitLabel",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Label_HasContent",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidRole",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidPropertyValue",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidIdRef",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_RequiredProperties",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_EmptyPropertyValue",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidProperty",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Aria_ImgAlt",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Canvas",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_Aria_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_RequiredChildren_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_RequiredParent_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_EventHandlerMissingRole_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_WidgetLabels_Implicit",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_ARIA_11_Guideline",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_List_Group_ListItem",level:a.eRulePolicy.VIOLATION},{id:"HAAC_ActiveDescendantCheck",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleToolbarUniqueLabel",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Must_Have_Text_Input",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_DOM_Focus",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Autocomplete",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Autocomplete_Invalid",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Expanded",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Popup",level:a.eRulePolicy.VIOLATION}]}]},{id:"WCAG_2_1",name:"WCAG 2.1 (A, AA)",category:a.eRuleCategory.ACCESSIBILITY,description:"Rules for WCAG 2.1 AA. This is the current W3C recommendation. Content that conforms to WCAG 2.1 also conforms to WCAG 2.0.",checkpoints:[{num:"1.1.1",name:"Non-text Content",summary:"All non-text content that is presented to the user has a text alternative that serves the equivalent purpose.",rules:[{id:"WCAG20_Input_ExplicitLabelImage",level:a.eRulePolicy.VIOLATION},{id:"RPT_Img_UsemapValid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Object_HasText",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Applet_HasAlt",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_ExplicitLabel",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Area_HasAlt",level:a.eRulePolicy.VIOLATION},{id:"RPT_Img_AltCommonMisuse",level:a.eRulePolicy.VIOLATION},{id:"RPT_Img_LongDescription2",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_HasAlt",level:a.eRulePolicy.VIOLATION},{id:"RPT_Pre_ASCIIArt",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_AudioVideoAltFilename",level:a.eRulePolicy.VIOLATION},{id:"RPT_Style_BackgroundImage",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_LinkTextNotRedundant",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_TitleEmptyWhenAltNull",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Img_UsemapAlt",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Text_Emoticons",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_PresentationImgHasNonNullAlt",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Figure_label",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_AltBrief",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Embed_HasNoEmbed",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Embed_HasAlt",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Style_Trigger2",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_BackgroundImg_HasTextOrTitle",level:a.eRulePolicy.RECOMMENDATION},{id:"Valerie_Noembed_HasContent",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_Canvas",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.1",name:"Audio-only and Video-only (Prerecorded)",summary:"For prerecorded audio-only or video-only media, an alternative provides equivalent information.",rules:[{id:"HAAC_Video_HasNoTrack",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_AudioTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.2",name:"Captions (Prerecorded)",summary:"Captions are provided for all prerecorded audio content in synchronized media.",rules:[{id:"HAAC_Video_HasNoTrack",level:a.eRulePolicy.VIOLATION}]},{num:"1.2.3",name:"Audio Description or Media Alternative (Prerecorded)",summary:"An alternative for time-based media or audio description of the prerecorded video content is provided for synchronized media.",rules:[{id:"RPT_Media_VideoReferenceTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.4",name:"Captions (Live)",summary:"Captions are provided for all live audio content in synchronized media.",rules:[{id:"HAAC_Video_HasNoTrack",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_VideoObjectTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.5",name:"Audio Description (Prerecorded)",summary:"Audio description is provided for all prerecorded video content in synchronized media.",rules:[{id:"RPT_Media_VideoReferenceTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.3.1",name:"Info and Relationships",summary:"Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.",rules:[{id:"RPT_Headers_FewWords",level:a.eRulePolicy.VIOLATION},{id:"RPT_Blockquote_HasCite",level:a.eRulePolicy.VIOLATION},{id:"RPT_Blockquote_WrapsTextQuote",level:a.eRulePolicy.VIOLATION},{id:"RPT_Block_ShouldBeHeading",level:a.eRulePolicy.VIOLATION},{id:"RPT_Label_UniqueFor",level:a.eRulePolicy.VIOLATION},{id:"RPT_List_UseMarkup",level:a.eRulePolicy.VIOLATION},{id:"RPT_Script_OnclickHTML1",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Fieldset_HasLegend",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Table_CapSummRedundant",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Table_Scope_Valid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_RadioChkInFieldSet",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Caption_HasContent",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Caption_InTable",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Table_DataCellRelationships",level:a.eRulePolicy.VIOLATION},{id:"RPT_Table_DataHeadingsAria",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Label_RefValid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Style_BeforeAfter",level:a.eRulePolicy.VIOLATION},{id:"RPT_List_Misuse",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Select_HasOptGroup",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Script_OnclickHTML2",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Input_InFieldSet",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Table_LayoutTrigger",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Table_Structure",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Table_SummaryAria3",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.3.2",name:"Meaningful Sequence",summary:"When the sequence in which content is presented affects its meaning, a correct reading sequence can be programmatically determined.",rules:[{id:"Valerie_Elem_DirValid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Text_LetterSpacing",level:a.eRulePolicy.VIOLATION}]},{num:"1.3.3",name:"Sensory Characteristics",summary:"Instructions provided for understanding and operating content do not rely solely on sensory characteristics of components such as shape, size, visual location, orientation, or sound.",rules:[{id:"RPT_Text_SensoryReference",level:a.eRulePolicy.VIOLATION}]},{num:"1.3.4",name:"Orientation",summary:"Content does not restrict its view and operation to a single display orientation, such as portrait or landscape.",rules:[]},{num:"1.3.5",name:"Identify Input Purpose",summary:"The purpose of each input field that collects information about the user can be programmatically determined when the field serves a common purpose.",rules:[{id:"WCAG21_Input_Autocomplete",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.1",name:"Use of Color",summary:"Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.",rules:[{id:"RPT_Font_ColorInForm",level:a.eRulePolicy.VIOLATION},{id:"RPT_Style_ColorSemantics1",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.2",name:"Audio Control",summary:"If any audio plays automatically for more than 3 seconds, either a mechanism is available to pause or stop the audio, or a mechanism is available to control audio volume independently from the overall system volume level.",rules:[{id:"RPT_Embed_AutoStart",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.3",name:"Contrast (Minimum)",summary:"The visual presentation of text and images of text has a contrast ratio of at least 4.5:1, with a 3:1 ratio for large-scale text.",rules:[{id:"IBMA_Color_Contrast_WCAG2AA",level:a.eRulePolicy.VIOLATION},{id:"IBMA_Color_Contrast_WCAG2AA_PV",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.4",name:"Resize Text",summary:"Text can be resized without assistive technology up to 200 percent without loss of content or functionality.",rules:[{id:"WCAG21_Style_Viewport",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.5",name:"Images of Text",summary:"If the technologies being used can achieve the visual presentation, text is used to convey information rather than images of text.",rules:[]},{num:"1.4.10",name:"Reflow",summary:"Content can reflow without loss of information or functionality, and without requiring scrolling in two dimensions.",rules:[]},{num:"1.4.11",name:"Non-text Contrast",summary:"The parts of graphical objects required to understand the content, and the visual information required to identify UI components and states, have a contrast ratio of at least 3:1 against adjacent colors.",rules:[]},{num:"1.4.12",name:"Text Spacing",summary:"No loss of content or functionality occurs when users change letter, word and paragraph spacing, as well as line height.",rules:[]},{num:"1.4.13",name:"Content on Hover or Focus",summary:"Where hover or focus actions cause additional content to become visible and hidden, the additional content is dismissable, hoverable and persistent.",rules:[]},{num:"2.1.1",name:"Keyboard",summary:"All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes.",rules:[{id:"RPT_Elem_EventMouseAndKey",level:a.eRulePolicy.RECOMMENDATION},{id:"Rpt_Aria_InvalidTabindexForActivedescendant",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MissingFocusableChild",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MissingKeyboardHandler",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Audio_Video_Trigger",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Application_Role_Text",level:a.eRulePolicy.VIOLATION}]},{num:"2.1.2",name:"No Keyboard Trap",summary:"If keyboard focus can be moved to a component using a keyboard interface, then focus can be moved away from that component using only a keyboard interface, and, if it requires more than unmodified arrow or tab keys or other standard exit methods, the user is advised of the method for moving focus away.",rules:[{id:"HAAC_Media_DocumentTrigger2",level:a.eRulePolicy.RECOMMENDATION}]},{num:"2.1.4",name:"Character Key Shortcuts",summary:"If a keyboard shortcut is implemented using only letter, punctuation, number or symbol characters, then the shortcut can be turned off, remapped or activated only on focus.",rules:[]},{num:"2.2.1",name:"Timing Adjustable",summary:"For each time limit that is set by the content, the user can turn off, adjust, or extend the limit.",rules:[{id:"RPT_Meta_Refresh",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Meta_RedirectZero",level:a.eRulePolicy.VIOLATION}]},{num:"2.2.2",name:"Pause, Stop, Hide",summary:"For moving, blinking, scrolling, or auto-updating information, the user can pause, stop, hide or adjust the information.",rules:[{id:"RPT_Marquee_Trigger",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Blink_AlwaysTrigger",level:a.eRulePolicy.VIOLATION},{id:"RPT_Blink_CSSTrigger1",level:a.eRulePolicy.VIOLATION}]},{num:"2.3.1",name:"Three Flashes or Below Threshold",summary:"Content does not contain anything that flashes more than three times in any one second period, or the flash is below the general flash and red flash thresholds.",rules:[]},{num:"2.4.1",name:"Bypass Blocks",summary:"A mechanism is available to bypass blocks of content that are repeated on multiple Web pages.",rules:[{id:"WCAG20_Frame_HasTitle",level:a.eRulePolicy.VIOLATION},{id:"RPT_Html_SkipNav",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Frame_SrcHtml",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleSearchLandmarks",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ComplementaryLandmarkLabel_Implicit",level:a.eRulePolicy.RECOMMENDATION},{id:"Rpt_Aria_MultipleApplicationLandmarks",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ApplicationLandmarkLabel",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleDocumentRoles",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Body_FirstASkips_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Body_FirstAContainsSkipText_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_OrphanedContent_Native_Host_Sematics",level:a.eRulePolicy.RECOMMENDATION},{id:"Rpt_Aria_RegionLabel_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleMainsVisibleLabel_Implicit",level:a.eRulePolicy.RECOMMENDATION},{id:"Rpt_Aria_MultipleBannerLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleComplementaryLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleContentinfoLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleFormLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleNavigationLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleArticleRoles_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleGroupRoles_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleContentinfoInSiblingSet_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_OneBannerInSiblingSet_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ContentinfoWithNoMain_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ComplementaryRequiredLabel_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleRegionsUniqueLabel_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleMainsRequireLabel_Implicit_2",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.2",name:"Page Titled",summary:"Web pages, non-web documents, and software have titles that describe topic or purpose.",rules:[{id:"WCAG20_Doc_HasTitle",level:a.eRulePolicy.VIOLATION},{id:"RPT_Title_Valid",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.3",name:"Focus Order",summary:"If content can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components receive focus in an order that preserves meaning and operability.",rules:[]},{num:"2.4.4",name:"Link Purpose (In Context)",summary:"The purpose of each link can be determined from the link text alone or from the link text together with its programmatically determined link content.",rules:[{id:"WCAG20_A_HasText",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.5",name:"Multiple Ways",summary:"More than one way is available to locate a Web page within a set of Web pages, except where the Web Page is the result of, or a step in, a process.",rules:[]},{num:"2.4.6",name:"Headings and Labels",summary:"Headings and labels describe topic or purpose.",rules:[{id:"RPT_Header_HasContent",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.7",name:"Focus Visible",summary:"Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible.",rules:[{id:"RPT_Style_HinderFocus1",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Script_FocusBlurs",level:a.eRulePolicy.VIOLATION}]},{num:"2.5.1",name:"Pointer Gestures",summary:"All functionality that uses multipoint or path-based gestures for operation can be operated with a single pointer without a path-based gesture.",rules:[]},{num:"2.5.2",name:"Pointer Cancellation",summary:"For functionality that can be operated using a single pointer, completion of the function is on the up-event with an ability to abort, undo or reverse the outcome.",rules:[]},{num:"2.5.3",name:"Label in Name",summary:"For user interface components with labels that include text or images of text, the accessible name contains the text that is presented visually.",rules:[{id:"WCAG21_Label_Accessible",level:a.eRulePolicy.VIOLATION}]},{num:"2.5.4",name:"Motion Actuation",summary:"Functionality that can be operated by motion can also be operated by user interface components, and the motion trigger can be disabled.",rules:[]},{num:"3.1.1",name:"Language of Page",summary:"The default human language of Web pages, non-Web documents, or software can be programmatically determined.",rules:[{id:"WCAG20_Html_HasLang",level:a.eRulePolicy.VIOLATION}]},{num:"3.1.2",name:"Language of Parts",summary:"The human language of each passage or phrase in the content can be programmatically determined.",rules:[{id:"WCAG20_Elem_Lang_Valid",level:a.eRulePolicy.VIOLATION}]},{num:"3.2.1",name:"On Focus",summary:"When any component receives focus, it does not initiate a change of context.",rules:[{id:"WCAG20_Select_NoChangeAction",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Script_FocusBlurs",level:a.eRulePolicy.VIOLATION}]},{num:"3.2.2",name:"On Input",summary:"Changing the setting of any user interface component does not automatically cause a change of context unless the user has been advised of the behavior before using the component.",rules:[{id:"WCAG20_A_TargetAndText",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Form_HasSubmit",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Form_TargetAndText",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_HasOnchange",level:a.eRulePolicy.VIOLATION},{id:"RPT_Form_ChangeEmpty",level:a.eRulePolicy.RECOMMENDATION}]},{num:"3.2.3",name:"Consistent Navigation",summary:"Navigational mechanisms that are repeated on multiple Web pages within a set of Web pages occur in the same relative order each time they are repeated, unless a change is initiated by the user.",rules:[]},{num:"3.2.4",name:"Consistent Identification",summary:"Components that have the same functionality within a set of Web pages are identified consistently.",rules:[]},{num:"3.3.1",name:"Error Identification",summary:"If an input error is automatically detected, the item that is in error is identified and the error is described to the user in text.",rules:[{id:"HAAC_Aria_ErrorMessage",level:a.eRulePolicy.VIOLATION}]},{num:"3.3.2",name:"Labels or Instructions",summary:"Labels or instructions are provided when content requires user input.",rules:[{id:"WCAG20_Input_LabelBefore",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_LabelAfter",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Accesskey_NeedLabel",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_Aria_Or_HTML5_Attr",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Input_Placeholder",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_VisibleLabel",level:a.eRulePolicy.VIOLATION}]},{num:"3.3.3",name:"Error Suggestion",summary:"If an input error is automatically detected and suggestions for correction are known, then the suggestions are provided to the user, unless it would jeopardize the security or purpose of the content.",rules:[]},{num:"3.3.4",name:"Error Prevention (Legal, Financial, Data)",summary:"For content that cause legal commitments or financial transactions for the user to occur, that modify or delete user-controllable data in data storage systems, or that submit user test responses, the user can reverse, correct, or confirm the action.",rules:[]},{num:"4.1.1",name:"Parsing",summary:"In content implemented using markup languages, elements have complete start and end tags, elements are nested according to their specifications, elements do not contain duplicate attributes, and any IDs are unique, except where the specifications allow these features.",rules:[{id:"RPT_Elem_UniqueId",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Elem_UniqueAccessKey",level:a.eRulePolicy.VIOLATION}]},{num:"4.1.2",name:"Name, Role, Value",summary:"For all user interface components (including, but not limited to: form elements, links and components generated by scripts), the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to user agents, including assistive technologies.",rules:[{id:"WCAG20_Input_ExplicitLabel",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Label_HasContent",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidRole",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidPropertyValue",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidIdRef",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_RequiredProperties",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_EmptyPropertyValue",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidProperty",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Aria_ImgAlt",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Canvas",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_Aria_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_RequiredChildren_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_RequiredParent_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_EventHandlerMissingRole_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_WidgetLabels_Implicit",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_ARIA_11_Guideline",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_List_Group_ListItem",level:a.eRulePolicy.VIOLATION},{id:"HAAC_ActiveDescendantCheck",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleToolbarUniqueLabel",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Must_Have_Text_Input",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_DOM_Focus",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Autocomplete",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Autocomplete_Invalid",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Expanded",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Popup",level:a.eRulePolicy.VIOLATION}]}]},{id:"WCAG_2_0",name:"WCAG 2.0 (A, AA)",category:a.eRuleCategory.ACCESSIBILITY,description:"Rules for WCAG 2.0 AA. Referenced by US Section 508, but not the latest W3C recommendation.",checkpoints:[{num:"1.1.1",name:"Non-text Content",summary:"All non-text content that is presented to the user has a text alternative that serves the equivalent purpose.",rules:[{id:"WCAG20_Input_ExplicitLabelImage",level:a.eRulePolicy.VIOLATION},{id:"RPT_Img_UsemapValid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Object_HasText",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Applet_HasAlt",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_ExplicitLabel",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Area_HasAlt",level:a.eRulePolicy.VIOLATION},{id:"RPT_Img_AltCommonMisuse",level:a.eRulePolicy.VIOLATION},{id:"RPT_Img_LongDescription2",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_HasAlt",level:a.eRulePolicy.VIOLATION},{id:"RPT_Pre_ASCIIArt",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_AudioVideoAltFilename",level:a.eRulePolicy.VIOLATION},{id:"RPT_Style_BackgroundImage",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_LinkTextNotRedundant",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_TitleEmptyWhenAltNull",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Img_UsemapAlt",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Text_Emoticons",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_PresentationImgHasNonNullAlt",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Figure_label",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_AltBrief",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Embed_HasNoEmbed",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Embed_HasAlt",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Style_Trigger2",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_BackgroundImg_HasTextOrTitle",level:a.eRulePolicy.RECOMMENDATION},{id:"Valerie_Noembed_HasContent",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_Canvas",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.1",name:"Audio-only and Video-only (Prerecorded)",summary:"For prerecorded audio-only or video-only media, an alternative provides equivalent information.",rules:[{id:"HAAC_Video_HasNoTrack",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_AudioTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.2",name:"Captions (Prerecorded)",summary:"Captions are provided for all prerecorded audio content in synchronized media.",rules:[{id:"HAAC_Video_HasNoTrack",level:a.eRulePolicy.VIOLATION}]},{num:"1.2.3",name:"Audio Description or Media Alternative (Prerecorded)",summary:"An alternative for time-based media or audio description of the prerecorded video content is provided for synchronized media.",rules:[{id:"RPT_Media_VideoReferenceTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.4",name:"Captions (Live)",summary:"Captions are provided for all live audio content in synchronized media.",rules:[{id:"HAAC_Video_HasNoTrack",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_VideoObjectTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.5",name:"Audio Description (Prerecorded)",summary:"Audio description is provided for all prerecorded video content in synchronized media.",rules:[{id:"RPT_Media_VideoReferenceTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.3.1",name:"Info and Relationships",summary:"Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.",rules:[{id:"RPT_Headers_FewWords",level:a.eRulePolicy.VIOLATION},{id:"RPT_Blockquote_HasCite",level:a.eRulePolicy.VIOLATION},{id:"RPT_Blockquote_WrapsTextQuote",level:a.eRulePolicy.VIOLATION},{id:"RPT_Block_ShouldBeHeading",level:a.eRulePolicy.VIOLATION},{id:"RPT_Label_UniqueFor",level:a.eRulePolicy.VIOLATION},{id:"RPT_List_UseMarkup",level:a.eRulePolicy.VIOLATION},{id:"RPT_Script_OnclickHTML1",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Fieldset_HasLegend",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Table_CapSummRedundant",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Table_Scope_Valid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_RadioChkInFieldSet",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Caption_HasContent",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Caption_InTable",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Table_DataCellRelationships",level:a.eRulePolicy.VIOLATION},{id:"RPT_Table_DataHeadingsAria",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Label_RefValid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Style_BeforeAfter",level:a.eRulePolicy.VIOLATION},{id:"RPT_List_Misuse",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Select_HasOptGroup",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Script_OnclickHTML2",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Input_InFieldSet",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Table_LayoutTrigger",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Table_Structure",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Table_SummaryAria3",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.3.2",name:"Meaningful Sequence",summary:"When the sequence in which content is presented affects its meaning, a correct reading sequence can be programmatically determined.",rules:[{id:"Valerie_Elem_DirValid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Text_LetterSpacing",level:a.eRulePolicy.VIOLATION}]},{num:"1.3.3",name:"Sensory Characteristics",summary:"Instructions provided for understanding and operating content do not rely solely on sensory characteristics of components such as shape, size, visual location, orientation, or sound.",rules:[{id:"RPT_Text_SensoryReference",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.1",name:"Use of Color",summary:"Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.",rules:[{id:"RPT_Font_ColorInForm",level:a.eRulePolicy.VIOLATION},{id:"RPT_Style_ColorSemantics1",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.2",name:"Audio Control",summary:"If any audio plays automatically for more than 3 seconds, either a mechanism is available to pause or stop the audio, or a mechanism is available to control audio volume independently from the overall system volume level.",rules:[{id:"RPT_Embed_AutoStart",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.3",name:"Contrast (Minimum)",summary:"The visual presentation of text and images of text has a contrast ratio of at least 4.5:1, with a 3:1 ratio for large-scale text.",rules:[{id:"IBMA_Color_Contrast_WCAG2AA",level:a.eRulePolicy.VIOLATION},{id:"IBMA_Color_Contrast_WCAG2AA_PV",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.4",name:"Resize Text",summary:"Text can be resized without assistive technology up to 200 percent without loss of content or functionality.",rules:[{id:"WCAG21_Style_Viewport",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.5",name:"Images of Text",summary:"If the technologies being used can achieve the visual presentation, text is used to convey information rather than images of text.",rules:[]},{num:"2.1.1",name:"Keyboard",summary:"All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes.",rules:[{id:"RPT_Elem_EventMouseAndKey",level:a.eRulePolicy.RECOMMENDATION},{id:"Rpt_Aria_InvalidTabindexForActivedescendant",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MissingFocusableChild",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MissingKeyboardHandler",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Audio_Video_Trigger",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Application_Role_Text",level:a.eRulePolicy.VIOLATION}]},{num:"2.1.2",name:"No Keyboard Trap",summary:"If keyboard focus can be moved to a component using a keyboard interface, then focus can be moved away from that component using only a keyboard interface, and, if it requires more than unmodified arrow or tab keys or other standard exit methods, the user is advised of the method for moving focus away.",rules:[{id:"HAAC_Media_DocumentTrigger2",level:a.eRulePolicy.RECOMMENDATION}]},{num:"2.2.1",name:"Timing Adjustable",summary:"For each time limit that is set by the content, the user can turn off, adjust, or extend the limit.",rules:[{id:"RPT_Meta_Refresh",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Meta_RedirectZero",level:a.eRulePolicy.VIOLATION}]},{num:"2.2.2",name:"Pause, Stop, Hide",summary:"For moving, blinking, scrolling, or auto-updating information, the user can pause, stop, hide or adjust the information.",rules:[{id:"RPT_Marquee_Trigger",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Blink_AlwaysTrigger",level:a.eRulePolicy.VIOLATION},{id:"RPT_Blink_CSSTrigger1",level:a.eRulePolicy.VIOLATION}]},{num:"2.3.1",name:"Three Flashes or Below Threshold",summary:"Content does not contain anything that flashes more than three times in any one second period, or the flash is below the general flash and red flash thresholds.",rules:[]},{num:"2.4.1",name:"Bypass Blocks",summary:"A mechanism is available to bypass blocks of content that are repeated on multiple Web pages.",rules:[{id:"WCAG20_Frame_HasTitle",level:a.eRulePolicy.VIOLATION},{id:"RPT_Html_SkipNav",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Frame_SrcHtml",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleSearchLandmarks",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ComplementaryLandmarkLabel_Implicit",level:a.eRulePolicy.RECOMMENDATION},{id:"Rpt_Aria_MultipleApplicationLandmarks",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ApplicationLandmarkLabel",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleDocumentRoles",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Body_FirstASkips_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Body_FirstAContainsSkipText_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_OrphanedContent_Native_Host_Sematics",level:a.eRulePolicy.RECOMMENDATION},{id:"Rpt_Aria_RegionLabel_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleMainsVisibleLabel_Implicit",level:a.eRulePolicy.RECOMMENDATION},{id:"Rpt_Aria_MultipleBannerLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleComplementaryLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleContentinfoLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleFormLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleNavigationLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleArticleRoles_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleGroupRoles_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleContentinfoInSiblingSet_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_OneBannerInSiblingSet_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ContentinfoWithNoMain_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ComplementaryRequiredLabel_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleRegionsUniqueLabel_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleMainsRequireLabel_Implicit_2",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.2",name:"Page Titled",summary:"Web pages, non-web documents, and software have titles that describe topic or purpose.",rules:[{id:"WCAG20_Doc_HasTitle",level:a.eRulePolicy.VIOLATION},{id:"RPT_Title_Valid",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.3",name:"Focus Order",summary:"If content can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components receive focus in an order that preserves meaning and operability.",rules:[]},{num:"2.4.4",name:"Link Purpose (In Context)",summary:"The purpose of each link can be determined from the link text alone or from the link text together with its programmatically determined link content.",rules:[{id:"WCAG20_A_HasText",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.5",name:"Multiple Ways",summary:"More than one way is available to locate a Web page within a set of Web pages, except where the Web Page is the result of, or a step in, a process.",rules:[]},{num:"2.4.6",name:"Headings and Labels",summary:"Headings and labels describe topic or purpose.",rules:[{id:"RPT_Header_HasContent",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.7",name:"Focus Visible",summary:"Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible.",rules:[{id:"RPT_Style_HinderFocus1",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Script_FocusBlurs",level:a.eRulePolicy.VIOLATION}]},{num:"3.1.1",name:"Language of Page",summary:"The default human language of Web pages, non-Web documents, or software can be programmatically determined.",rules:[{id:"WCAG20_Html_HasLang",level:a.eRulePolicy.VIOLATION}]},{num:"3.1.2",name:"Language of Parts",summary:"The human language of each passage or phrase in the content can be programmatically determined.",rules:[{id:"WCAG20_Elem_Lang_Valid",level:a.eRulePolicy.VIOLATION}]},{num:"3.2.1",name:"On Focus",summary:"When any component receives focus, it does not initiate a change of context.",rules:[{id:"WCAG20_Select_NoChangeAction",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Script_FocusBlurs",level:a.eRulePolicy.VIOLATION}]},{num:"3.2.2",name:"On Input",summary:"Changing the setting of any user interface component does not automatically cause a change of context unless the user has been advised of the behavior before using the component.",rules:[{id:"WCAG20_A_TargetAndText",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Form_HasSubmit",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Form_TargetAndText",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_HasOnchange",level:a.eRulePolicy.VIOLATION},{id:"RPT_Form_ChangeEmpty",level:a.eRulePolicy.RECOMMENDATION}]},{num:"3.2.3",name:"Consistent Navigation",summary:"Navigational mechanisms that are repeated on multiple Web pages within a set of Web pages occur in the same relative order each time they are repeated, unless a change is initiated by the user.",rules:[]},{num:"3.2.4",name:"Consistent Identification",summary:"Components that have the same functionality within a set of Web pages are identified consistently.",rules:[]},{num:"3.3.1",name:"Error Identification",summary:"If an input error is automatically detected, the item that is in error is identified and the error is described to the user in text.",rules:[{id:"HAAC_Aria_ErrorMessage",level:a.eRulePolicy.VIOLATION}]},{num:"3.3.2",name:"Labels or Instructions",summary:"Labels or instructions are provided when content requires user input.",rules:[{id:"WCAG20_Input_LabelBefore",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_LabelAfter",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Accesskey_NeedLabel",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_Aria_Or_HTML5_Attr",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Input_Placeholder",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_VisibleLabel",level:a.eRulePolicy.VIOLATION}]},{num:"3.3.3",name:"Error Suggestion",summary:"If an input error is automatically detected and suggestions for correction are known, then the suggestions are provided to the user, unless it would jeopardize the security or purpose of the content.",rules:[]},{num:"3.3.4",name:"Error Prevention (Legal, Financial, Data)",summary:"For content that cause legal commitments or financial transactions for the user to occur, that modify or delete user-controllable data in data storage systems, or that submit user test responses, the user can reverse, correct, or confirm the action.",rules:[]},{num:"4.1.1",name:"Parsing",summary:"In content implemented using markup languages, elements have complete start and end tags, elements are nested according to their specifications, elements do not contain duplicate attributes, and any IDs are unique, except where the specifications allow these features.",rules:[{id:"RPT_Elem_UniqueId",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Elem_UniqueAccessKey",level:a.eRulePolicy.VIOLATION}]},{num:"4.1.2",name:"Name, Role, Value",summary:"For all user interface components (including, but not limited to: form elements, links and components generated by scripts), the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to user agents, including assistive technologies.",rules:[{id:"WCAG20_Input_ExplicitLabel",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Label_HasContent",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidRole",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidPropertyValue",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidIdRef",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_RequiredProperties",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_EmptyPropertyValue",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidProperty",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Aria_ImgAlt",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Canvas",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_Aria_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_RequiredChildren_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_RequiredParent_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_EventHandlerMissingRole_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_WidgetLabels_Implicit",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_ARIA_11_Guideline",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_List_Group_ListItem",level:a.eRulePolicy.VIOLATION},{id:"HAAC_ActiveDescendantCheck",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleToolbarUniqueLabel",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Must_Have_Text_Input",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_DOM_Focus",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Autocomplete",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Autocomplete_Invalid",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Expanded",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Popup",level:a.eRulePolicy.VIOLATION}]}]},{id:"IBM_Accessibility_BETA",name:"IBM Accessibility BETA",category:a.eRuleCategory.ACCESSIBILITY,description:"Rules for WCAG 2.1 AA plus additional IBM checklist supplemental requirements and experimental rules.",checkpoints:[{num:"1.1.1",name:"Non-text Content",summary:"All non-text content that is presented to the user has a text alternative that serves the equivalent purpose.",rules:[{id:"WCAG20_Input_ExplicitLabelImage",level:a.eRulePolicy.VIOLATION},{id:"RPT_Img_UsemapValid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Object_HasText",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Applet_HasAlt",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_ExplicitLabel",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Area_HasAlt",level:a.eRulePolicy.VIOLATION},{id:"RPT_Img_AltCommonMisuse",level:a.eRulePolicy.VIOLATION},{id:"RPT_Img_LongDescription2",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_HasAlt",level:a.eRulePolicy.VIOLATION},{id:"RPT_Pre_ASCIIArt",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_AudioVideoAltFilename",level:a.eRulePolicy.VIOLATION},{id:"RPT_Style_BackgroundImage",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_LinkTextNotRedundant",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_TitleEmptyWhenAltNull",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Img_UsemapAlt",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Text_Emoticons",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Img_PresentationImgHasNonNullAlt",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Figure_label",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_AltBrief",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Embed_HasNoEmbed",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Embed_HasAlt",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Style_Trigger2",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_BackgroundImg_HasTextOrTitle",level:a.eRulePolicy.RECOMMENDATION},{id:"Valerie_Noembed_HasContent",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_Canvas",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.1",name:"Audio-only and Video-only (Prerecorded)",summary:"For prerecorded audio-only or video-only media, an alternative provides equivalent information.",rules:[{id:"HAAC_Video_HasNoTrack",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_AudioTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.2",name:"Captions (Prerecorded)",summary:"Captions are provided for all prerecorded audio content in synchronized media.",rules:[{id:"HAAC_Video_HasNoTrack",level:a.eRulePolicy.VIOLATION}]},{num:"1.2.3",name:"Audio Description or Media Alternative (Prerecorded)",summary:"An alternative for time-based media or audio description of the prerecorded video content is provided for synchronized media.",rules:[{id:"RPT_Media_VideoReferenceTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.4",name:"Captions (Live)",summary:"Captions are provided for all live audio content in synchronized media.",rules:[{id:"HAAC_Video_HasNoTrack",level:a.eRulePolicy.VIOLATION},{id:"RPT_Media_VideoObjectTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.2.5",name:"Audio Description (Prerecorded)",summary:"Audio description is provided for all prerecorded video content in synchronized media.",rules:[{id:"RPT_Media_VideoReferenceTrigger",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.3.1",name:"Info and Relationships",summary:"Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.",rules:[{id:"RPT_Headers_FewWords",level:a.eRulePolicy.VIOLATION},{id:"RPT_Blockquote_HasCite",level:a.eRulePolicy.VIOLATION},{id:"RPT_Blockquote_WrapsTextQuote",level:a.eRulePolicy.VIOLATION},{id:"RPT_Block_ShouldBeHeading",level:a.eRulePolicy.VIOLATION},{id:"RPT_Label_UniqueFor",level:a.eRulePolicy.VIOLATION},{id:"RPT_List_UseMarkup",level:a.eRulePolicy.VIOLATION},{id:"RPT_Script_OnclickHTML1",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Fieldset_HasLegend",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Table_CapSummRedundant",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Table_Scope_Valid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_RadioChkInFieldSet",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Caption_HasContent",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Caption_InTable",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Table_DataCellRelationships",level:a.eRulePolicy.VIOLATION},{id:"RPT_Table_DataHeadingsAria",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Label_RefValid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Style_BeforeAfter",level:a.eRulePolicy.VIOLATION},{id:"RPT_List_Misuse",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Select_HasOptGroup",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Script_OnclickHTML2",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Input_InFieldSet",level:a.eRulePolicy.RECOMMENDATION},{id:"RPT_Table_LayoutTrigger",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Table_Structure",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Table_SummaryAria3",level:a.eRulePolicy.RECOMMENDATION}]},{num:"1.3.2",name:"Meaningful Sequence",summary:"When the sequence in which content is presented affects its meaning, a correct reading sequence can be programmatically determined.",rules:[{id:"Valerie_Elem_DirValid",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Text_LetterSpacing",level:a.eRulePolicy.VIOLATION}]},{num:"1.3.3",name:"Sensory Characteristics",summary:"Instructions provided for understanding and operating content do not rely solely on sensory characteristics of components such as shape, size, visual location, orientation, or sound.",rules:[{id:"RPT_Text_SensoryReference",level:a.eRulePolicy.VIOLATION}]},{num:"1.3.4",name:"Orientation",summary:"Content does not restrict its view and operation to a single display orientation, such as portrait or landscape.",rules:[]},{num:"1.3.5",name:"Identify Input Purpose",summary:"The purpose of each input field that collects information about the user can be programmatically determined when the field serves a common purpose.",rules:[{id:"WCAG21_Input_Autocomplete",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.1",name:"Use of Color",summary:"Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.",rules:[{id:"RPT_Font_ColorInForm",level:a.eRulePolicy.VIOLATION},{id:"RPT_Style_ColorSemantics1",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.2",name:"Audio Control",summary:"If any audio plays automatically for more than 3 seconds, either a mechanism is available to pause or stop the audio, or a mechanism is available to control audio volume independently from the overall system volume level.",rules:[{id:"RPT_Embed_AutoStart",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.3",name:"Contrast (Minimum)",summary:"The visual presentation of text and images of text has a contrast ratio of at least 4.5:1, with a 3:1 ratio for large-scale text.",rules:[{id:"IBMA_Color_Contrast_WCAG2AA",level:a.eRulePolicy.VIOLATION},{id:"IBMA_Color_Contrast_WCAG2AA_PV",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.4",name:"Resize Text",summary:"Text can be resized without assistive technology up to 200 percent without loss of content or functionality.",rules:[{id:"WCAG21_Style_Viewport",level:a.eRulePolicy.VIOLATION}]},{num:"1.4.5",name:"Images of Text",summary:"If the technologies being used can achieve the visual presentation, text is used to convey information rather than images of text.",rules:[]},{num:"1.4.10",name:"Reflow",summary:"Content can reflow without loss of information or functionality, and without requiring scrolling in two dimensions.",rules:[]},{num:"1.4.11",name:"Non-text Contrast",summary:"The parts of graphical objects required to understand the content, and the visual information required to identify UI components and states, have a contrast ratio of at least 3:1 against adjacent colors.",rules:[]},{num:"1.4.12",name:"Text Spacing",summary:"No loss of content or functionality occurs when users change letter, word and paragraph spacing, as well as line height.",rules:[]},{num:"1.4.13",name:"Content on Hover or Focus",summary:"Where hover or focus actions cause additional content to become visible and hidden, the additional content is dismissable, hoverable and persistent.",rules:[]},{num:"2.1.1",name:"Keyboard",summary:"All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes.",rules:[{id:"RPT_Elem_EventMouseAndKey",level:a.eRulePolicy.RECOMMENDATION},{id:"Rpt_Aria_InvalidTabindexForActivedescendant",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MissingFocusableChild",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MissingKeyboardHandler",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Audio_Video_Trigger",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Application_Role_Text",level:a.eRulePolicy.VIOLATION}]},{num:"2.1.2",name:"No Keyboard Trap",summary:"If keyboard focus can be moved to a component using a keyboard interface, then focus can be moved away from that component using only a keyboard interface, and, if it requires more than unmodified arrow or tab keys or other standard exit methods, the user is advised of the method for moving focus away.",rules:[{id:"HAAC_Media_DocumentTrigger2",level:a.eRulePolicy.RECOMMENDATION}]},{num:"2.1.4",name:"Character Key Shortcuts",summary:"If a keyboard shortcut is implemented using only letter, punctuation, number or symbol characters, then the shortcut can be turned off, remapped or activated only on focus.",rules:[]},{num:"2.2.1",name:"Timing Adjustable",summary:"For each time limit that is set by the content, the user can turn off, adjust, or extend the limit.",rules:[{id:"RPT_Meta_Refresh",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Meta_RedirectZero",level:a.eRulePolicy.VIOLATION}]},{num:"2.2.2",name:"Pause, Stop, Hide",summary:"For moving, blinking, scrolling, or auto-updating information, the user can pause, stop, hide or adjust the information.",rules:[{id:"RPT_Marquee_Trigger",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Blink_AlwaysTrigger",level:a.eRulePolicy.VIOLATION},{id:"RPT_Blink_CSSTrigger1",level:a.eRulePolicy.VIOLATION}]},{num:"2.3.1",name:"Three Flashes or Below Threshold",summary:"Content does not contain anything that flashes more than three times in any one second period, or the flash is below the general flash and red flash thresholds.",rules:[]},{num:"2.4.1",name:"Bypass Blocks",summary:"A mechanism is available to bypass blocks of content that are repeated on multiple Web pages.",rules:[{id:"WCAG20_Frame_HasTitle",level:a.eRulePolicy.VIOLATION},{id:"RPT_Html_SkipNav",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Frame_SrcHtml",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleSearchLandmarks",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ComplementaryLandmarkLabel_Implicit",level:a.eRulePolicy.RECOMMENDATION},{id:"Rpt_Aria_MultipleApplicationLandmarks",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ApplicationLandmarkLabel",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleDocumentRoles",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Body_FirstASkips_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Body_FirstAContainsSkipText_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_OrphanedContent_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_RegionLabel_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleMainsVisibleLabel_Implicit",level:a.eRulePolicy.RECOMMENDATION},{id:"Rpt_Aria_MultipleBannerLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleComplementaryLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleContentinfoLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleFormLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleNavigationLandmarks_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleArticleRoles_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleGroupRoles_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleContentinfoInSiblingSet_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_OneBannerInSiblingSet_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ContentinfoWithNoMain_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ComplementaryRequiredLabel_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleRegionsUniqueLabel_Implicit",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleMainsRequireLabel_Implicit_2",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.2",name:"Page Titled",summary:"Web pages, non-web documents, and software have titles that describe topic or purpose.",rules:[{id:"WCAG20_Doc_HasTitle",level:a.eRulePolicy.VIOLATION},{id:"RPT_Title_Valid",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.3",name:"Focus Order",summary:"If content can be navigated sequentially and the navigation sequences affect meaning or operation, focusable components receive focus in an order that preserves meaning and operability.",rules:[{id:"IBMA_Focus_Tabbable",level:a.eRulePolicy.VIOLATION},{id:"IBMA_Focus_MultiTab",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.4",name:"Link Purpose (In Context)",summary:"The purpose of each link can be determined from the link text alone or from the link text together with its programmatically determined link content.",rules:[{id:"WCAG20_A_HasText",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.5",name:"Multiple Ways",summary:"More than one way is available to locate a Web page within a set of Web pages, except where the Web Page is the result of, or a step in, a process.",rules:[]},{num:"2.4.6",name:"Headings and Labels",summary:"Headings and labels describe topic or purpose.",rules:[{id:"RPT_Header_HasContent",level:a.eRulePolicy.VIOLATION}]},{num:"2.4.7",name:"Focus Visible",summary:"Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible.",rules:[{id:"RPT_Style_HinderFocus1",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Script_FocusBlurs",level:a.eRulePolicy.VIOLATION}]},{num:"2.5.1",name:"Pointer Gestures",summary:"All functionality that uses multipoint or path-based gestures for operation can be operated with a single pointer without a path-based gesture.",rules:[]},{num:"2.5.2",name:"Pointer Cancellation",summary:"For functionality that can be operated using a single pointer, completion of the function is on the up-event with an ability to abort, undo or reverse the outcome.",rules:[]},{num:"2.5.3",name:"Label in Name",summary:"For user interface components with labels that include text or images of text, the accessible name contains the text that is presented visually.",rules:[{id:"WCAG21_Label_Accessible",level:a.eRulePolicy.VIOLATION}]},{num:"2.5.4",name:"Motion Actuation",summary:"Functionality that can be operated by motion can also be operated by user interface components, and the motion trigger can be disabled.",rules:[]},{num:"3.1.1",name:"Language of Page",summary:"The default human language of Web pages, non-Web documents, or software can be programmatically determined.",rules:[{id:"WCAG20_Html_HasLang",level:a.eRulePolicy.VIOLATION}]},{num:"3.1.2",name:"Language of Parts",summary:"The human language of each passage or phrase in the content can be programmatically determined.",rules:[{id:"WCAG20_Elem_Lang_Valid",level:a.eRulePolicy.VIOLATION}]},{num:"3.2.1",name:"On Focus",summary:"When any component receives focus, it does not initiate a change of context.",rules:[{id:"WCAG20_Select_NoChangeAction",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Script_FocusBlurs",level:a.eRulePolicy.VIOLATION}]},{num:"3.2.2",name:"On Input",summary:"Changing the setting of any user interface component does not automatically cause a change of context unless the user has been advised of the behavior before using the component.",rules:[{id:"WCAG20_A_TargetAndText",level:a.eRulePolicy.RECOMMENDATION},{id:"WCAG20_Form_HasSubmit",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Form_TargetAndText",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_HasOnchange",level:a.eRulePolicy.VIOLATION},{id:"RPT_Form_ChangeEmpty",level:a.eRulePolicy.RECOMMENDATION}]},{num:"3.2.3",name:"Consistent Navigation",summary:"Navigational mechanisms that are repeated on multiple Web pages within a set of Web pages occur in the same relative order each time they are repeated, unless a change is initiated by the user.",rules:[]},{num:"3.2.4",name:"Consistent Identification",summary:"Components that have the same functionality within a set of Web pages are identified consistently.",rules:[]},{num:"3.3.1",name:"Error Identification",summary:"If an input error is automatically detected, the item that is in error is identified and the error is described to the user in text.",rules:[{id:"HAAC_Aria_ErrorMessage",level:a.eRulePolicy.VIOLATION}]},{num:"3.3.2",name:"Labels or Instructions",summary:"Labels or instructions are provided when content requires user input.",rules:[{id:"WCAG20_Input_LabelBefore",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_LabelAfter",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Accesskey_NeedLabel",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_Aria_Or_HTML5_Attr",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Input_Placeholder",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Input_VisibleLabel",level:a.eRulePolicy.VIOLATION}]},{num:"3.3.3",name:"Error Suggestion",summary:"If an input error is automatically detected and suggestions for correction are known, then the suggestions are provided to the user, unless it would jeopardize the security or purpose of the content.",rules:[]},{num:"3.3.4",name:"Error Prevention (Legal, Financial, Data)",summary:"For content that cause legal commitments or financial transactions for the user to occur, that modify or delete user-controllable data in data storage systems, or that submit user test responses, the user can reverse, correct, or confirm the action.",rules:[]},{num:"4.1.1",name:"Parsing",summary:"In content implemented using markup languages, elements have complete start and end tags, elements are nested according to their specifications, elements do not contain duplicate attributes, and any IDs are unique, except where the specifications allow these features.",rules:[{id:"RPT_Elem_UniqueId",level:a.eRulePolicy.VIOLATION},{id:"WCAG20_Elem_UniqueAccessKey",level:a.eRulePolicy.VIOLATION}]},{num:"4.1.2",name:"Name, Role, Value",summary:"For all user interface components (including, but not limited to: form elements, links and components generated by scripts), the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to user agents, including assistive technologies.",rules:[{id:"WCAG20_Input_ExplicitLabel",level:a.eRulePolicy.VIOLATION},{id:"Valerie_Label_HasContent",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidRole",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidPropertyValue",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidIdRef",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_RequiredProperties",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_EmptyPropertyValue",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_ValidProperty",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Aria_ImgAlt",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Canvas",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_Aria_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_RequiredChildren_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_RequiredParent_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_EventHandlerMissingRole_Native_Host_Sematics",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_WidgetLabels_Implicit",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_ARIA_11_Guideline",level:a.eRulePolicy.RECOMMENDATION},{id:"HAAC_List_Group_ListItem",level:a.eRulePolicy.VIOLATION},{id:"HAAC_ActiveDescendantCheck",level:a.eRulePolicy.VIOLATION},{id:"Rpt_Aria_MultipleToolbarUniqueLabel",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Must_Have_Text_Input",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_DOM_Focus",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Autocomplete",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Autocomplete_Invalid",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Expanded",level:a.eRulePolicy.VIOLATION},{id:"HAAC_Combobox_Popup",level:a.eRulePolicy.VIOLATION}]}]}];t.a11yRulesets=l},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.designRulesets=void 0;t.designRulesets=[]}]); \ No newline at end of file diff --git a/cypress-accessibility-checker/src/lib/.gitignore b/cypress-accessibility-checker/src/lib/.gitignore new file mode 100644 index 000000000..7e5ce920e --- /dev/null +++ b/cypress-accessibility-checker/src/lib/.gitignore @@ -0,0 +1,2 @@ +engine +reporters/genReport.js \ No newline at end of file diff --git a/cypress-accessibility-checker/src/lib/ACCommands.js b/cypress-accessibility-checker/src/lib/ACCommands.js new file mode 100644 index 000000000..f5a50530a --- /dev/null +++ b/cypress-accessibility-checker/src/lib/ACCommands.js @@ -0,0 +1,1410 @@ +const ACConfigLoader = require("./ACConfigLoader"); +const ACMetricsLogger = require('./log/ACMetricsLogger'); +const ACReporterJSON = require("./reporters/ACReporterJSON"); +const ACReporterHTML = require("./reporters/ACReporterHTML"); +const ACReporterCSV = require("./reporters/ACReporterCSV"); +const DeepDiff = require("deep-diff"); + +const myrequest = (url) => { + if (typeof cy !== "undefined") { + return cy.request(url) + .then((data) => { + return data.body; + }) + } else { + return new Promise((resolve, reject) => { + const request = require("request"); + request.get(url, function (error, response, body) { + if (error) { + reject(error); + } else { + resolve(JSON.parse(body)); + } + }); + }); + } +} + +let loggerFunction = function (output) { + ACCommands.DEBUG && console.log(output); +}; + +let loggerCreate = function (type) { + return logger; +}; + +let logger = { + debug: loggerFunction, + info: loggerFunction, + error: loggerFunction, + warn: loggerFunction, + create: loggerCreate +}; + +function areValidPolicy(valPolicies, curPol) { + let isValPol = false; + let errorPolicy = ""; + + for (let i = 0; i < curPol.length; ++i) { + if (valPolicies.indexOf(curPol[i]) === -1) { + errorPolicy += "" + curPol[i] + ","; + } else { + isValPol = true; + } + + } + if (errorPolicy.length > 0) { + errorPolicy = errorPolicy.substr(0, errorPolicy.length - 1); + console.log(`[WARN] InvalidPolicies: Invalid policies "${errorPolicy}". Valid policy ids are: ${valPolicies}`); + } + if (!isValPol) { + console.error(`[ERROR] ValidPoliciesMissing: No valid policy has been provided. Valid policy ids for the specified archive are: ${valPolicies}`); + process.exit(-1); + } +} + +let ace; + +let ACCommands = module.exports = { + DEBUG: false, + initializeConfig: () => { + if (ACCommands.initConfigOnce) return ACCommands.initConfigOnce; + + ACCommands.DEBUG && console.log("START 'initialize' function"); + return ACCommands.initConfigOnce = ACConfigLoader() + .then((config) => { + ACCommands.Config = config; + return config; + }); + }, + initialize: () => { + if (ACCommands.initOnce) { + return ACCommands.initOnce.then(() => { + if (!ACCommands.ace) { + ACCommands.initOnce = false; + return ACCommands.initialize(); + } + }) + } + + return ACCommands.initOnce = ACCommands.initializeConfig().then(() => { + if (ACCommands.Config.rulePack.includes("localhost")) { + process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; + } + + ACCommands.reporterHTML = new ACReporterHTML(ACCommands); + ACCommands.reporterJSON = new ACReporterJSON(ACCommands); + ACCommands.reporterCSV = new ACReporterCSV(ACCommands); + + // Specify if debug information should be printed or not + ACCommands.DEBUG = ACCommands.Config.DEBUG; + + // Array that contains the list of entries that need to be compared between the actual and baseline objects only. + // Note: This is used by the cleanComplianceObjectBeforeCompare function to filter the report based on this. + ACCommands.baselineIssueList = ["ruleId", "xpath"]; + ACCommands.metricsLogger = new ACMetricsLogger("cypress-accessibility-checker", logger, ACCommands.Config.policies.join(",")); + + // Initialize the scanSummary object with summary information for accessibility-checker + ACCommands.scanSummary = ACCommands.initializeSummary(); + + // Initialize the global object which will store all the diff results for a scan that is run, using + // actual and expected. + ACCommands.diffResults = {}; + + // Initialize the global object which will store all the scan results indexed by the label. + ACCommands.scanResults = {}; + + ACCommands.engineLoaded = false; + return ACCommands.loadEngine() + }) + .then(() => { + ACCommands.DEBUG && console.log("END 'initialize' function"); + }) + }, + initializeSummary: () => { + // Variable Decleration + let scanSummary = {}; + let reportLevels = ACCommands.Config.reportLevels; + + // Initialize counts + scanSummary.counts = {}; + + // In the case that report levels are provided then populate the count object in + // scanSummary.counts object with the levels which were provided in reportLevels + // array. + if (reportLevels) { + + // Iterate over the report levels and populate the pageResultsWithCount counts + // object + reportLevels.forEach(function (levels) { + scanSummary.counts[levels] = 0; + }); + scanSummary.counts.ignored = 0; + } + // Populate the scanSummary.counts object with all the levels + else { + scanSummary.counts = { + "violation": 0, + "potentialviolation": 0, + "recommendation": 0, + "potentialrecommendation": 0, + "manual": 0, + "pass": 0 + }; + } + + // Add Start time when this script is loaded into browser + // Start time will be in milliseconds elapsed since 1 January 1970 00:00:00 UTC up until now. + scanSummary.startReport = Date.now(); + + // Leave end report as empty for now + scanSummary.endReport = ''; + + // Add the toolID, policies, reportLevels, failLevels and labels from the config to the summary + scanSummary.toolID = ACCommands.Config.toolID; + scanSummary.policies = ACCommands.Config.policies.join(","); + scanSummary.reportLevels = ACCommands.Config.reportLevels; + scanSummary.labels = ACCommands.Config.label; + scanSummary.failLevels = ACCommands.Config.failLevels; + + // Add scanID (UUID) to the scan summary object + scanSummary.scanID = ACCommands.Config.scanID; + + // Build the paceScanSummary object which will contains all the items that were scanned and a count + // summary for each of the scanned items. + scanSummary.pageScanSummary = []; + + return scanSummary; + }, + /** + * This function loads the compliance engine. + * @param {Function} callback - Provide callback function which will be executed once the engine is loaded + * + * @return N/A - This function will not return any thing, as it is full async + */ + loadEngine: (content) => { + ACCommands.DEBUG && console.log("START 'aChecker.loadEngine' function"); + + if (typeof cy === "undefined") { + // We aren't loading the engine in tasks outside of the cypress browser engine + if (ace) { + return Promise.resolve(); + } else { + return module.exports.loadLocalEngine((engine) => { + return ace = engine; + }); + } + } + return cy.window() + .then((win) => { + ACCommands.myWindow = win.parent; + if (!ACCommands.myWindow.ace) { + return ACCommands.loadCypressEngine() + .then(() => { + ace = win.ace = ACCommands.myWindow.ace; + win.ace.checker = new ace.Checker(); + ACCommands.DEBUG && console.log("END 'aChecker.loadEngine' function1"); + }) + } else { + ace = win.ace = ACCommands.myWindow.ace; + ACCommands.DEBUG && console.log("END 'aChecker.loadEngine' function2"); + } + }); + }, + + loadCypressEngine: () => { + ACCommands.DEBUG && console.log("START 'aChecker.loadCypressEngine' function"); + if (ace) { + ACCommands.DEBUG && console.log("END 'aChecker.loadCypressEngine' function"); + return Promise.resolve(); + } + return myrequest(ACCommands.Config.rulePack + "/ace.js") + .then((data) => { + const script = ACCommands.myWindow.document.createElement("script"); + script.innerHTML = data; + ACCommands.myWindow.document.documentElement.appendChild(script); + ace = ACCommands.myWindow.ace; + ACCommands.DEBUG && console.log("END 'aChecker.loadCypressEngine' function"); + }) + }, + /** + * This function is responsible for checking if the provided label is unique or not. + * + * @param {String} label - Provide the label which should be checked if it exists or not + * + * @return {boolean} labelExists - return false if the label is not unique, otherwise return true + * + * PRIVATE METHOD + * + * @memberOf this + */ + isLabelUnique: (label) => { + ACCommands.DEBUG && console.log("START 'aChecker.isLabelUnique' function"); + + // Variable Decleration + let labelExists = false; + + ACCommands.DEBUG && console.log("Checking if label: " + label + " is unique."); + + // Check if the label that is provided was already used or not, by simply calling the some API on the array + // and passing it a callback function which checks if the label exists in the global paceScanSummary object. + labelExists = ACCommands.scanSummary.pageScanSummary.some(function (scanSummary) { + return scanSummary.label === label; + }); + + ACCommands.DEBUG && console.log("END 'aChecker.isLabelUnique' function"); + + return !labelExists; + }, + /** + * This function is responsible performing a scan based on the context that is provided, following are + * the supported context type: + * Single node (HTMLElement) + * Local file path (String) + * URL (String) + * document node + * data stream for html content (String) + * + * Future Items + * Multiple node (Array of HTMLElements) ---> FUTURE + * + * @param {(String|HTMLElement|DocumentNode)} content - Provide the context to scan, which includes the items from above. + * @param {String} label - Provide a label for the scan that is being performed + * @param {Function} callback - Provide callback function which will be executed once the results are extracted. + * + * @return N/A - This function will not return any thing, as it is full asyn so scan will be performed and the call back + * function which was provided will be called which will perform the verification or anything that is needed. + * + * PUBLIC API + * + * @memberOf this + */ + getCompliance: function (content, label, callback) { + if (callback) { + ACCommands.getComplianceHelper(content, label) + .then((result) => { + callback(result.report, result.webdriver); + }); + } else { + return ACCommands.getComplianceHelper(content, label); + } + }, + + getComplianceHelper: function (content, label) { + ACCommands.DEBUG && console.log("START 'ACCommands.getComplianceHelper' function"); + return ACCommands.initialize().then(() => { + if (!content) { + console.error("aChecker: Unable to get compliance of null or undefined object") + return null; + } + + // In the case that the label is null or undefined, throw an error using the karma API + // console.error with the message of the error. + if (label === null || typeof label === "undefined" || label === undefined) { + + // Variable Decleration + let testcaseWhichIsMissingRequiredLabel = null; + let generalErrorMessageLabelNotUnique = "\n[Error] labelNotProvided: Label must be provided when calling aChecker.getCompliance."; + + // Get the caller of the aChecker.getCompliance function which will be the testcase that is calling this function + // This way we can make it the error more descriptive and would help the user identify where the issues is. + // We have to build and throw an Error() object and then using the try/catch to catch this error and then extract the + // stack and parse it to get the 2nd element in the stack which will be the caller of this function which will be the + // testcase which called this function. + try { + // Throw Error() object + throw new Error(); + } catch (exception) { + // Extract the stack trace from the error object and parse it to get the single one caller up which will be the 2nd index + testcaseWhichIsMissingRequiredLabel = exception.stack.split("\n")[1]; + + // Call the Karma error API, to send message to the Karma server that there was an error on the client side + console.error("Label was not provided at: " + testcaseWhichIsMissingRequiredLabel + generalErrorMessageLabelNotUnique); + } + } + + // Check to make sure that the label that is provided is unique with all the other ones + // that we have gone through. + let labelUnique = ACCommands.isLabelUnique(label); + + // In the case that the label is not unique + if (!labelUnique) { + // Variable Decleration dependencies/tools-rules-html/v2/a11y/test/g471/Table-DataNoSummaryARIA.html + let testcaseDoesNotUseUniqueLabel = null; + let generalErrorMessageLabelNotUnique = "\n[Error] labelNotUnique: Label provided to aChecker.getCompliance should be unique across all testcases in a single accessibility-checker session."; + + // Get the caller of the aChecker.getCompliance function which will be the testcase that is calling this function + // This way we can make it the error more descriptive and would help the user identify where the issues is. + // We have to build and throw an Error() object and then using the try/catch to catch this error and then extract the + // stack and parse it to get the 2nd element in the stack which will be the caller of this function which will be the + // testcase which called this function. + try { + // Throw Error() object + throw new Error(); + } catch (exception) { + // Extract the stack trace from the error object and parse it to get the single one caller up which will be the 2nd index + testcaseDoesNotUseUniqueLabel = exception.stack.split("\n")[1]; + + // Call the Karma error API, to send message to the Karma server that there was an error on the client side + console.error("Label \"" + label + "\" provided at: " + testcaseDoesNotUseUniqueLabel + " is not unique." + generalErrorMessageLabelNotUnique); + } + } + + // Get the Data when the scan is started + // Start time will be in milliseconds elapsed since 1 January 1970 00:00:00 UTC up until now. + let policies = ACCommands.Config.policies; + let curPol = null; + if (policies) { + curPol = JSON.parse(JSON.stringify(policies)); + } + + ACCommands.DEBUG && console.log("getComplianceHelper:Cypress"); + return ACCommands.getComplianceHelperCypress(label, content, curPol); + }) + }, + + getComplianceHelperCypress: (label, parsed, curPol) => { + try { + let startScan = Date.now(); + let checker = new ace.Checker(); + return checker.check(parsed, ACCommands.Config.policies) + .then(function (report) { + for (const result of report.results) { + delete result.node; + } + return report; + }) + .then(function (report) { + if (curPol != null && !ACCommands.Config.checkPolicy) { + let valPolicies = new ace.Checker().rulesetIds; + ACCommands.Config.checkPolicy = true; + areValidPolicy(valPolicies, curPol); + } + + // If there is something to report... + let origReport = null; + if (report.results) { + let url = parsed.location && parsed.location.href; + report.summary = report.summary || {} + report.summary.URL = url; + report.counts = {} + + origReport = JSON.parse(JSON.stringify(report)); + origReport = ACCommands.buildReport(origReport, url, label, startScan); + + // Filter the violations based on the reporLevels + report = ACCommands.filterViolations(report); + + // Add the count object, to data a recount after the filtering of violations is done. + report = ACCommands.updateViolationCount(report); + + // Add the violation count to global summary object + ACCommands.addToSummaryCount(report.counts); + + // Build the report object for this scan, to follow a specific format. Refer to the + // function prolog for more information on the object creation. + report = ACCommands.buildReport(report, url, label, startScan); + + // Add the scan results to global karma result object which can be accessed when users testcase + // finishes, user can also access it to alter it for any reason. + ACCommands.addResultsToGlobal(report); + + } + + ACCommands.DEBUG && console.log("getComplianceHelperCypress:",report); + + return { + "origReport": origReport, + "report": report + }; + }); + } catch (err) { + console.error(err); + return Promise.reject(err); + }; + }, + /** + * This function is responsible for building the results object in a specific format which will be provided back to + * the user to do any thing they want to do with it. (compare, print it, save to db, etc...) + * + * Note: This function converts it to match with the following format outlined at: + * https://github.com/IBMa/equal-access/tree/master/karma-accessibility-checker + * + * @param {Object} results - The results object which we need to build the report based on, following is the format the + * object needs to follow: + * { + * "report": { + * "numChecked": 227, + * "numTrigger": 1, + * "ruleTime": 5, + * "totalTime": 8, + * "issues": [ + * { + * "severityCode": "eISHigh", + * "messageCode": "rpt.g377.elemUniqueId", + * "ruleId": "377", + * "help": "idhi_accessibility_check_g377.html", + * "msgArgs": [ + * "div", + * "firstDiv" + * ], + * "xpath": "/html[1]/body[1]/div[2]/div[2]", + * "snippet": "<div id=\"firstDiv\">", + * "bounds": { + * "left": 10, + * "top": 181, + * "height": 0, + * "width": 1249 + * }, + * "level": "violation" + * } + * ], + * "docTitle": "Helo World" + * }, + * "counts": { + * "violation": 1, + * "potentialviolation": 0, + * "recommendation": 0, + * "potentialrecommendation": 0, + * "manual": 0 + * }, + * "issueMessages": { + * "messages": { + * "rpt.g377.elemUniqueId": "The {0} element has the id \"{1}\" that is either empty or already in use." + * }, + * "lang": "en-us" + * } + * } + * + * @param {String} URL - The URL which the report is being built for + * @param {String} label - A label to identify what this report is going to be for, in the case not using URL or local files. + * @param {String} startScan - The start time of the scan. + * + * @return {Object} results - return the formatted results based in the following format: + * + * { + * "scanID": "ef3aec68-f073-4f9c-b372-421ae00bd55d", + * "toolID": "karma-ibma-v1.0.0", + * "summary": { + * "counts": { + * "violation": 5, + * "potentialviolation": 0, + * "recommendation": 5, + * "potentialrecommendation": 0, + * "manual": 1 + * }, + * "scanTime": 80, + * "policies": [ + * "CI162_5_2_DCP080115" + * ], + * "reportLevels": [ + * "violation", + * "potentialviolation", + * "recommendation", + * "potentialrecommendation", + * "manual" + * ], + * "startScan": "2016-06-06T00:52:41.603Z" + * }, + * "URL": "", + * "label": "unitTestContent", + * "screenshot": "<placeholder>", + * "issueMessages": { + * "messages": { + * "rpt.g377.elemUniqueId": "The {0} element has the id \"{1}\" that is either empty or already in use." + * }, + * "lang": "en-us" + * }, + * "reports": [ + * { + * "frameIdx": "0", + * "frameTitle": "Frame 0", + * "issues": [ + * { + * "severity": "Low", + * "message": "If style sheets are ignored or unsupported, ensure that pages are still readable and usable.", + * "messageCode": "rpt.g1.styleTrigger", + * "ruleId": "1", + * "help": "idhi_accessibility_check_g1.html", + * "msgArgs": [], + * "bounds": { + * "left": 0, + * "top": 0, + * "height": 0, + * "width": 0 + * }, + * "level": "manual", + * "xpath": "/html[1]/head[1]/style[1]", + * "snippet": "<style type=\"text/css\">" + * } + * .... + * ] + * }, + * { + * "frameIdx": "1", + * "frameTitle": "Frame 1", + * "issues": [ + * { + * "severity": "High", + * "message": "The table element with WAI-ARIA presentation role has structural element(s) and/or attribute(s) td.", + * "messageCode": "rpt.g471.tableStructure", + * "ruleId": "471", + * "help": "idhi_accessibility_check_g471.html", + * "msgArgs": [ + * "table", + * "td" + * ], + * "bounds": { + * "left": 10, + * "top": 990, + * "height": 219, + * "width": 335 + * }, + * "level": "violation", + * "xpath": "/html[1]/body[1]/div[2]/table[3]", + * "snippet": "<table id=\"layout_table3\" role=\"presentation\">" + * } + * .... + * ] + * } + * ] + * } + * + * PRIVATE METHOD + * + * @memberOf this + */ + buildReport: function (report, URL, label, startScan) { + if (report.report) { + report = report.report; + } + // Build the scan summary object which will be added to the build report + // Note: This summary is only for this single scan. + report.summary = { + counts: report.counts, + scanTime: report.totalTime, + ruleArchive: ACCommands.Config.ruleArchive, + policies: ACCommands.Config.policies, + reportLevels: ACCommands.Config.reportLevels, + startScan: startScan + }; + + // Add scanID (UUID) to the individual pages + report.scanID = ACCommands.Config.scanID; + + // Add toolID to the individual pages + report.toolID = ACCommands.Config.toolID; + + // Add the URL to the object it it is defined + if (URL !== null && typeof URL !== "undefined") { + report.summary.URL = URL; + } + + // Add the label to the result object, label should always be + // defined no matter what as it is required to be provided by the user. + report.label = label; + + // Add the screenshot base64 object to the results object + // TODO: Find a way to actually extract the screenshot, since karma + // allows the use of any browesr, some browser do not allow taking screenshot + // so would have to alalyze which browser allow it and take it for only those. + // PhantonJS, any selenium drived browser. + //results.screenshot = "<placeholder>"; + + // Clean up the results object + delete report.counts; + delete report.ruleTime; + delete report.totalTime; + + if (ACCommands.Config.disableIgnore === undefined || ACCommands.Config.disableIgnore == false || ACCommands.Config.disableIgnore === null) { + // set ignore:true for previously seen violations + // retrieve baseline + let baselineReport = ACCommands.getBaseline(label); + + // set ignore:true for previously seen violations and set ignore to false if no ignore fields exist yet + if (baselineReport) { + report = ACCommands.ignoreExtraBaselineViolations(report, baselineReport); + } + else { //add ignored field + report.summary.counts.ignored = 0; + } + } + + let lvlIdx = { + "violation": 1, + "potentialviolation": 2, + "recommendation": 3, + "potentialrecommendation": 4, + "manual": 5, + "pass": 6 + }; + + report.results.sort(function (a, b) { + let aLvl = lvlIdx[a.level]; + let bLvl = lvlIdx[b.level]; + if (!aLvl) aLvl = 7; + if (!bLvl) bLvl = 7; + return aLvl != bLvl && aLvl - bLvl || + b.ruleId != a.ruleId && b.ruleId - a.ruleId || + b.path.dom - a.path.dom; + }); + + return report; + }, + /** + * This function is responsible for getting the baseline object for a label that was provided. + * + * @param {String} label - Provide a lable for which to get the baseline for. + * + * @return {Object} - return the baseline object from global space based on label provided, the object will be + * in the same format as outlined in the return of aChecker.buildReport function. + * + * PUBLIC API + * + * @memberOf this + */ + getBaseline: function (label) { + return ACCommands.baselines[label] || null; + }, + setBaselines: function (data) { + ACCommands.baselines = ACCommands.baselines || {}; + for (const key in data) { + ACCommands.baselines[key] = data[key]; + } + }, + ignoreExtraBaselineViolations: function (actualReport, baselineReport) { + let result = null; + let existingRuleIDs = []; + // Using for loop to make is sync code + let ignoredCount = 0; + let changedCounts = actualReport.summary.counts; + + let currentActualReport = actualReport.results; + const currentBaselineReport = baselineReport; + // a report exists in the baseline for the iframe + if (currentBaselineReport && currentBaselineReport.length === 1) { + let legacyBaseline = !!currentBaselineReport[0].issues; + for (const issue of currentActualReport) { + let currentRuleID = issue.ruleId; + let currentLevel = issue.level; + let currentXPATH = issue.path.dom; + //check if the issue exists in baseline already + let result = + legacyBaseline && currentBaselineReport[0].issues.filter(issue => issue.ruleId in aCheckerCypress.ruleIdToLegacyId && aCheckerCypress.ruleIdToLegacyId[issue.ruleId] === currentRuleID && issue.level === currentLevel && issue.xpath === currentXPATH) + || !legacyBaseline && currentBaselineReport.results.filter(issue => issue.ruleId === currentRuleID && issue.level === currentLevel && issue.dom.path === currentXPATH); + if (result && result.length !== 0) { + //violation exists in baseline, add ignore:true + issue.ignored = true; + ignoredCount++; + if (issue.level === "violation") { + changedCounts.violation--; + } + if (issue.level === "potentialviolation") { + changedCounts.potentialviolation--; + } + if (issue.level === "recommendation") { + changedCounts.recommendation--; + } + if (issue.level === "potentialrecommendation") { + changedCounts.potentialrecommendation--; + } + if (issue.level === "manual") { + changedCounts.manual--; + } + if (issue.level === "pass") { + changedCounts.pass--; + } + + } else { + issue.ignored = false; + } + } + + } + + // adding ignore count to summary + changedCounts.ignored = ignoredCount; + actualReport.summary.counts = changedCounts + return actualReport; + }, + /** + * This function is responsible for filtering the violations so that, only the violations levels that + * are provided in reportLevels are presented in the report. + * + * TODO: Possibly we can add this to the engine, so that the results are not provided by the engine + * when user has provided the reportLevels object. + * + * @param {Object} results - Provide the violation results, which follow the following format: + * { + * "report": { + * "numChecked": 227, + * "numTrigger": 1, + * "ruleTime": 5, + * "totalTime": 8, + * "issues": [ + * { + * "severityCode": "eISHigh", + * "messageCode": "rpt.g377.elemUniqueId", + * "ruleId": "377", + * "help": "idhi_accessibility_check_g377.html", + * "msgArgs": [ + * "div", + * "firstDiv" + * ], + * "xpath": "/html[1]/body[1]/div[2]/div[2]", + * "snippet": "<div id=\"firstDiv\">", + * "bounds": { + * "left": 10, + * "top": 181, + * "height": 0, + * "width": 1249 + * }, + * "level": "violation" + * } + * ], + * "docTitle": "Helo World" + * }, + * "counts": { + * "level.violation": 1, + * "level.potentialviolation": 0, + * "level.recommendation": 0, + * "level.potentialrecommendation": 0, + * "level.manual": 0 + * }, + * "issueMessages": { + * "messages": { + * "rpt.g377.elemUniqueId": "The {0} element has the id \"{1}\" that is either empty or already in use." + * }, + * "lang": "en-us" + * } + * } + * + * @return {Object} results - return results object which only contains the violation that were requested, + * follows the following format: + * { + * "report": { + * "numChecked": 227, + * "numTrigger": 1, + * "ruleTime": 5, + * "totalTime": 8, + * "issues": [ + * { + * "severityCode": "eISHigh", + * "messageCode": "rpt.g377.elemUniqueId", + * "ruleId": "377", + * "help": "idhi_accessibility_check_g377.html", + * "msgArgs": [ + * "div", + * "firstDiv" + * ], + * "xpath": "/html[1]/body[1]/div[2]/div[2]", + * "snippet": "<div id=\"firstDiv\">", + * "bounds": { + * "left": 10, + * "top": 181, + * "height": 0, + * "width": 1249 + * }, + * "level": "violation" + * } + * ], + * "docTitle": "Helo World" + * }, + * "counts": { + * "level.violation": 1, + * "level.potentialviolation": 0, + * "level.recommendation": 0, + * "level.potentialrecommendation": 0, + * "level.manual": 0 + * }, + * "issueMessages": { + * "messages": { + * "rpt.g377.elemUniqueId": "The {0} element has the id \"{1}\" that is either empty or already in use." + * }, + * "lang": "en-us" + * } + * } + * + * The return object is pretty much filtered failures (results.report.fail), wrapped around another object with extra frameIdx value. + * + * PRIVATE METHOD + * + * @memberOf this + */ + filterViolations: function (report) { + + // Variable Decleration + let reportLevels = ACCommands.Config.reportLevels; + let pageResults = report.results; + for (let iDis = 0; ACCommands.Config.disable && iDis < ACCommands.Config.disable.length; ++iDis) { + ACCommands.Config.disable[iDis] = "" + ACCommands.Config.disable[iDis]; + } + // Loop over all the violations and filter them, if the violation level does not match with, what user has + // requested to be reported. Also handle hidden at this point right now. + // TODO: Posible to filter the results directly in the engine, to avoid the need to do all this in each of the tools. + for (let i = 0; i < pageResults.length; ++i) { + + // Set the default ignore value to false if disableIgnore field in config file is not true + if (ACCommands.Config.disableIgnore === undefined || ACCommands.Config.disableIgnore == false || ACCommands.Config.disableIgnore === null){ + pageResults[i].ignored = false; + } + if (ACCommands.Config.disable && ACCommands.Config.disable.indexOf(pageResults[i].ruleId) !== -1) { + pageResults.splice(i--, 1); + continue; + } + // Remove violation which are not in the reportLevels + if (reportLevels) { + // Fetch the level from the results + let reportLevel = pageResults[i].value; + if (reportLevel[1] === "PASS") { + reportLevel = "pass"; + } else if ((reportLevel[0] === "VIOLATION" || reportLevel[0] === "RECOMMENDATION") && reportLevel[1] === "MANUAL") { + reportLevel = "manual"; + } else if (reportLevel[0] === "VIOLATION") { + if (reportLevel[1] === "FAIL") { + reportLevel = "violation"; + } else if (reportLevel[1] === "POTENTIAL") { + reportLevel = "potentialviolation"; + } + } else if (reportLevel[0] === "RECOMMENDATION") { + if (reportLevel[1] === "FAIL") { + reportLevel = "recommendation"; + } else if (reportLevel[1] === "POTENTIAL") { + reportLevel = "potentialrecommendation"; + } + } + pageResults[i].level = reportLevel; + + // Make sure the level is actually defined before trying to perform any action on it + if (reportLevel !== null && typeof reportLevel !== "undefined") { + // Remove the violation from the object if report level is not in the reportLevels array. + if (reportLevels.indexOf(reportLevel) === -1) { + pageResults.splice(i--, 1); + continue; + } + } else { + // In the case that level is null or not found remove this violation from the results. + pageResults.splice(i--, 1); + } + } + } + + return report; + }, + /** + * This function is responsible for iterating over all the issue elements and updating the counts object. + * + * @param {Object} pageResults - Provide the page results object, in the following format: + * { + * "report": { + * "numChecked": 227, + * "numTrigger": 1, + * "ruleTime": 5, + * "totalTime": 8, + * "issues": [ + * { + * "severityCode": "eISHigh", + * "messageCode": "rpt.g377.elemUniqueId", + * "ruleId": "377", + * "help": "idhi_accessibility_check_g377.html", + * "msgArgs": [ + * "div", + * "firstDiv" + * ], + * "xpath": "/html[1]/body[1]/div[2]/div[2]", + * "snippet": "<div id=\"firstDiv\">", + * "bounds": { + * "left": 10, + * "top": 181, + * "height": 0, + * "width": 1249 + * }, + * "level": "violation" + * } + * ], + * "docTitle": "Helo World" + * }, + * "counts": { + * "level.violation": 1, + * "level.potentialviolation": 0, + * "level.recommendation": 0, + * "level.potentialrecommendation": 0, + * "level.manual": 0 + * }, + * "issueMessages": { + * "messages": { + * "rpt.g377.elemUniqueId": "The {0} element has the id \"{1}\" that is either empty or already in use." + * }, + * "lang": "en-us" + * } + * } + * ...... + * + * @return {Object} pageResults - return the results object with the count object updated + * { + * "report": { + * "numChecked": 227, + * "numTrigger": 1, + * "ruleTime": 5, + * "totalTime": 8, + * "issues": [ + * { + * "severityCode": "eISHigh", + * "messageCode": "rpt.g377.elemUniqueId", + * "ruleId": "377", + * "help": "idhi_accessibility_check_g377.html", + * "msgArgs": [ + * "div", + * "firstDiv" + * ], + * "xpath": "/html[1]/body[1]/div[2]/div[2]", + * "snippet": "<div id=\"firstDiv\">", + * "bounds": { + * "left": 10, + * "top": 181, + * "height": 0, + * "width": 1249 + * }, + * "level": "violation" + * } + * ], + * "docTitle": "Helo World" + * }, + * "counts": { + * "level.violation": 1, + * "level.potentialviolation": 0, + * "level.recommendation": 0, + * "level.potentialrecommendation": 0, + * "level.manual": 0 + * }, + * "issueMessages": { + * "messages": { + * "rpt.g377.elemUniqueId": "The {0} element has the id \"{1}\" that is either empty or already in use." + * }, + * "lang": "en-us" + * } + * } + * + * PRIVATE METHOD + * + * @memberOf this + */ + updateViolationCount: function (report) { + + // Variable Decleration + let reportLevels = ACCommands.Config.reportLevels; + + // Build violation count object which will contain the updated count based on filter which + // which occured in filterViolations function. + let violationCount = {}; + + // In the case that report levels are provided then populate the count object in + // violationCount object with the levels which were provided in reportLevels + // array/ + if (reportLevels) { + + // Iterate over the report levels and populate the pageResultsWithCount counts + // object + reportLevels.forEach(function (levels) { + violationCount[levels] = 0; + }); + + } + // Populate the pageResultsWithCount counts object with all the levels + else { + violationCount = { + "violation": 0, + "potentialviolation": 0, + "recommendation": 0, + "potentialrecommendation": 0, + "manual": 0, + "pass": 0 + }; + } + + // Iterate over the page results + for (const item of report.results) { + if (item.level in violationCount) { + ++violationCount[item.level]; + } + } + + // Update the results count object with the new one which considers filtered results + report.counts = violationCount; + + return report; + }, + /** + * This function is responsible for updating/creating the global violation summary for the engine karma run + * for browser that it is running on. Will take the pageCount object which is part of the page object and + * add extract the values for each of the levels and add them to the global object. This will provide an overall + * summary of violations for all testcases run and all scans done. + * + * @param {Object} pageCount - Provide the page count object, in the following format: + * + * @return N/A - Global summary object is updated with the counts + * + * PRIVATE METHOD + * + * @memberOf this + */ + addToSummaryCount: function (pageCount) { + + // Variable Decleration + let ACScanSummary = ACCommands.scanSummary.counts || {}; + let addedToSummary = false; + + // In the case ACScanSummary is empty, simply assign pageCount to ACScanSummary + if (Object.keys(ACScanSummary).length === 0) { + + // Set pageCount as the summary count + ACScanSummary = pageCount; + + addedToSummary = true; + } + + // In the case that this is not first scan, handle adding up the summary + if (!addedToSummary) { + // Go through the pageCount object and for each of the levels, extract the value + // and add it to the accessibility-checker violation summary object. + // This will keep track of an overall summary of the violations for all testscases, that + // were run for a single karma run. + for (let level in pageCount) { + ACScanSummary[level] += pageCount[level]; + } + } + + // Assign the new violation summary back to the global object + ACCommands.scanSummary.counts = ACScanSummary; + }, + /** + * This function is responsible for indexing the results into global spaces based on label. + * + * @param {Object} results - Results object which will be provided to the user/wroten to the file. + * Refer to aChecker.buildReport function's return to figure out what the object + * will look like. + * + * @return - N/A - Global object is updated with the results + * + * PRIVATE METHOD + * + * @memberOf this + */ + addResultsToGlobal: function (results) { + + // Build the single page summary object to follow the following format: + // "label": "dependencies/tools-rules-html/v2/a11y/test/g471/Table-DataNoSummaryARIA.html", + // "counts": { + // "violation": 1, + // "potentialviolation": 0, + // "recommendation": 0, + // "potentialrecommendation": 0, + // "manual": 0 + // } + let pageSummaryObject = { + label: results.label, + counts: results.summary.counts + }; + + // Add the summary count for this scan to the pageScanSummary object which is in the global space + // Index this by the label. + ACCommands.scanSummary.pageScanSummary.push(pageSummaryObject); + + // Add the scan results to global space + ACCommands.scanResults[results.label] = results; + }, + /** + * This function is responsible for comparing the scan results with baseline or checking that there are + * no violations which fall into the failsLevels levels. In the case a baseline is found then baseline will + * be used to perform the check, in the case no baseline is provided then we comply with only failing if + * there is a sinble violation which falls into failLevels. + * + * @param {Object} actual - the actual results object provided by the user, this object should follow the + * same format as outlined in the return of aChecker.buildReport function. + * + * @return {int} - return 0 in the case actual matches baseline or no violations fall into failsLevels, + * return 1 in the case actual results does not match baseline results, + * return 2 in the case that there is a failure based on failLevels (this means no baseline found). + * return -1 in the case that there is an exception that occured in the results object which came from the scan engine. + * + * PUBLIC API + * + * @memberOf this + */ + assertCompliance: function (actualResults) { + if (actualResults.report) { + actualResults = actualResults.report; + } + return ACCommands.initializeConfig().then(() => { + // In the case that the details object contains Error object, this means that the scan engine threw an + // exception, therefore we should not compare results just fail instead. + if (actualResults.details instanceof Error) { + return -1; + } + + // Get the label directly from the results object, the same label has to match + // the baseline object which is available in the global space. + let label = actualResults.label; + + // Fetch the baseline object based on the label provided + let expected = ACCommands.getBaseline(label); + + // In the case there are no baseline found then run a different assertion algo, + // when there is baseline compare the baselines in the case there is no baseline then + // check to make sure there are no violations that are listed in the fails on. + if (expected !== null && typeof (expected) !== "undefined") { + // Run the diff algo to get the list of differences + return ACCommands.diffResultsWithExpected(actualResults, expected, true).then((differences) => { + + // In the case that there are no differences then that means it passed + if (differences === null || typeof (differences) === "undefined") { + return 0; + } else { + // Re-sort results and check again + let modActual = JSON.parse(JSON.stringify(actualResults.results)); + modActual.sort((a, b) => { + let cc = b.category.localeCompare(a.category); + if (cc != 0) return cc; + let pc = b.path.dom.localeCompare(a.path.dom); + if (pc !== 0) return pc; + return b.ruleId.localeCompare(a.ruleId); + }) + let modExpected = JSON.parse(JSON.stringify(expected.results)); + modExpected.sort((a, b) => { + let cc = b.category.localeCompare(a.category); + if (cc != 0) return cc; + let pc = b.path.dom.localeCompare(a.path.dom); + if (pc !== 0) return pc; + return b.ruleId.localeCompare(a.ruleId); + }) + return ACCommands.diffResultsWithExpected({ + results: modActual, + summary: actualResults.summary + }, { + results: modExpected , + summary: expected.summary + }, true).then((differences2) => { + if (differences2 === null || typeof (differences2) === "undefined") { + return 0; + } else { + // In the case that there are failures add the whole diff array to + // global space indexed by the label so that user can access it. + ACCommands.diffResults[label] = differences; + + return 1; + } + }) + } + }) + } else { + // In the case that there was no baseline data found compare the results based on + // the failLevels array, which was defined by the user. + let returnCode = ACCommands.compareBasedOnFailLevels(actualResults); + + // In the case there are no violations that match the fail on then return as success + if (returnCode === 0) { + return returnCode; + } else { + // In the case there are some violation that match in the fail on then return 2 + // to identify that there was a failure, and we used a 2nd method for compare. + return 2; + } + } + }) + }, + /** + * This function is responsible for checking if any of the issues reported have any level that falls + * into the failsLevel array. + * + * @param {Object} results - Provide the scan results, object which would be in the + * the same format as outlined in the return of aChecker.buildReport function. + * + * @return {int} - return 1 in the case a single issue was found which is in the failsLevel array. + * return -1 in the case that there is an exception that occured in the results object which came from the scan engine. + * + * PRIVATE METHOD + * + * @memberOf this + */ + compareBasedOnFailLevels: function (report) { + + // In the case that the details object contains Error object, this means that the scan engine through an + // exception, therefore we should not compare results just fail instead. + if (report.details instanceof Error) { + return -1; + } + + // Variable Decleration + let failLevels = ACCommands.Config.failLevels; + // Loop over all the issues to check for any level that is in failLevels + for (const issue of report.results) { + // In the case current level is in the failsLevel array them fail, with out checking further + // currently we are not saving exactly which results failed, as all the issues are going to be saved to + // results file. + if (failLevels.indexOf(issue.level) > -1) { + // return 1 as there was a fialure + return 1; + } + } + + // return 0 as there were no levels that fall into the failLevels + return 0; + }, + + /** + * This function is responsible for comparing actual with expected and returning all the differences as an array. + * + * @param {Object} actual - Provide the actual object to be used for compare + * @param {Object} expected - Provide the expected object to be used for compare + * @param {boolean} clean - Provide a boolean if both the actual and expected objects need to be cleaned + * cleaning refers to converting the objects to match with a basic compliance + * compare of xpath and ruleId. + * + * @return {Object} differences - return an array of diff objects that were found, following is the format of the object: + * [ + * { + * "kind": "E", + * "path": [ + * "reports", + * 0, + * "issues", + * 10, + * "xpath" + * ], + * "lhs": "/html[1]/body[1]/div[2]/table[5]", + * "rhs": "/html[1]/body[1]/div[2]/table[5]d", + * }, + * { + * "kind": "E", + * "path": [ + * "label" + * ], + * "lhs": "Table-layoutMultiple", + * "rhs": "dependencies/tools-rules-html/v2/a11y/test/g471/Table-layoutMultiple.html", + * } + * ] + * + * PUBLIC API + * + * @memberOf this + */ + diffResultsWithExpected: function (actual, expected, clean) { + return ACCommands.initialize().then(() => { + // In the case clean is set to true then run the cleanComplianceObjectBeforeCompare function on + // both the actual and expected objects passed in. This is to make sure that the objcet follow a + // simalar structure before compareing the objects. + if (clean) { + // Clean actual and expected objects + actual = ACCommands.cleanComplianceObjectBeforeCompare(actual); + expected = ACCommands.cleanComplianceObjectBeforeCompare(expected); + } + + // Run Deep diff function to compare the actual and expected values. + let differences = DeepDiff.default.diff(actual, expected); + + // Return the results of the diff, which will include the differences between the objects + if (!differences) return null; + + return differences; + }) + }, + /** + * This function is responsible for cleaning up the compliance baseline or actual results, based on + * a pre-defined set of criterias, such as the following: + * 1. No need to compare summary object + * 2. Only need to compare the ruleId and xpath in for each of the issues + * + * @param {Object} objectToClean - Provide either an baseline or actual results object which would be in the + * the same format as outlined in the return of aChecker.buildReport function. + * + * @return {Object} objectToClean - return an object that was cleaned to only contain the information that is + * needed for compare. Following is a sample of how the cleaned object will look like: + * { + * "label": "unitTestContent", + * "reports": [ + * { + * "frameIdx": "0", + * "frameTitle": "Frame 0", + * "issues": [ + * { + * "ruleId": "1", + * "xpath": "/html[1]/head[1]/style[1]" + * } + * .... + * ] + * }, + * { + * "frameIdx": "1", + * "frameTitle": "Frame 1", + * "issues": [ + * { + * "ruleId": "471", + * "xpath": "/html[1]/body[1]/div[2]/table[3]" + * } + * .... + * ] + * } + * ] + * } + * + * PRIVATE METHOD + * + * @memberOf this + */ + cleanComplianceObjectBeforeCompare: function (objectToClean) { + // Clone the object so that we do not reference the original or else it causes the original + // results object or baseline object to get updated, which we do not want as users are allowed + // access to the raw results object and baseline object. + // Convert the object into string and then parse it as a JSON object which will lose its reference + objectToClean = JSON.parse(JSON.stringify(objectToClean)); + + // Remove the summary object, scanID, toolID, issueMessage + delete objectToClean.summary; + delete objectToClean.nls; + delete objectToClean.scanID; + delete objectToClean.toolID; + delete objectToClean.issueMessages; + delete objectToClean.numExecuted; + + + // Loop over all the issues and remove the keys that are not needed for the compare + // Only leave the ruleId and xpath keys for compare. + for (let idx = 0; idx < objectToClean.results.length; ++idx) { + const issue = objectToClean.results[idx]; + if (issue.level === "pass") { + objectToClean.results.splice(idx--, 1); + } else { + issue.xpath = issue.path.dom; + // Loop over all the keys in a single issue object and remove all the + // keys that are not needed for compare + Object.keys(issue).forEach(function (key) { + // Remove all the keys which are not in the baselineIssueList + if (ACCommands.baselineIssueList.indexOf(key) === -1) { + delete issue[key]; + } + }); + // Make sure that the xpath in the case there is a [1] we replace it with "" + // to support some browser which return it differently + issue.xpath = issue.xpath.replace(/\[1\]/g, ""); + } + }; + + return objectToClean; + }, + /** + * This function is responsible for getting the diff results based on label for a scan that was already performed. + * + * @param {String} label - Provide a lable for which to get the diff results for. + * + * @return {Object} - return the diff results object from global space based on label provided, the object will be + * in the same format as outlined in the return of aChecker.diffResultsWithExpected function. + * + * PUBLIC API + * + * @memberOf this + */ + getDiffResults: function (label) { + if (!ACCommands.diffResults || !ACCommands.diffResults[label]) return null; + return ACCommands.diffResults[label]; + } +} \ No newline at end of file diff --git a/cypress-accessibility-checker/src/lib/ACConfigLoader.js b/cypress-accessibility-checker/src/lib/ACConfigLoader.js new file mode 100644 index 000000000..abfb92b7e --- /dev/null +++ b/cypress-accessibility-checker/src/lib/ACConfigLoader.js @@ -0,0 +1,385 @@ +/****************************************************************************** + Copyright:: 2020- IBM, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *****************************************************************************/ + // Load all the modules that are needed +var pathLib = require('path'); +var fs = require('fs'); +var YAML = require('js-yaml'); +var constants = require("./ACConstants"); +var uuid = require('uuid'); +var request = require("request"); + +const myrequest = (url) => { + if (typeof cy !== "undefined") { + return cy.request(url) + .then((data) => { + return data.body; + }) + } else { + return new Promise((resolve, reject) => { + request.get(url, function (error, response, body) { + if (error) { + reject(error); + } else { + resolve(JSON.parse(body)); + } + }); + }); + } +} + +/** + * This function is responsible converting policies into an Array based on string or Array. + * + * i.e. convert array into string version of array + * "CI162_5_2_DCP070116,CI162_5_2_DCP070116" + * + * converts to: + * + * [ + * "CI162_5_2_DCP070116", + * "CI162_5_2_DCP070116" + * ] + * + * @param {Array or String} policies - Provide list of policies, single policy or comma seperated policies + * + * @return {Array} policies - return the policy converted into Array version + * + * @memberOf this + */ +function convertPolicies(policies) { + constants.DEBUG && console.log("START 'convertPolicies' function"); + + constants.DEBUG && console.log("Converting policy provided to Array: "); + constants.DEBUG && console.log(policies); + + constants.DEBUG && console.log("END 'convertPolicies' function"); + + // In the case policies is an Array return it as engine expects list + if (policies instanceof Array) { + return policies; + } + // If the policies is string, we need to convert it to an array. Which includes comma seperated string support also + else if (typeof policies === "string") { + return policies.split(','); + } + + return policies; +} + +/** + * This function is responsible processing the achecker config which was initialized to make sure it contains, + * information which matches what the engine reads. + * + * i.e. + * Need to change reportLevels and failLevels to match with level declerations in the engine. + * replace violation with level.violation + * Need to change array of policies into a string + * ["CI162_5_2_DCP070116","CI162_5_2_DCP070116"] to "CI162_5_2_DCP070116,CI162_5_2_DCP070116" + * + * @param {Object} ACConfig - Provide the config object in which needs to be processed. + * + * @return {Object} ACConfig - return the config object which has been made engine readable + * + * @memberOf this + */ +function processACConfig(ACConfig) { + constants.DEBUG && console.log("START 'processACConfig' function"); + + // Convert the reportLevels and failLevels to match with what the engine provides + // Don't need to convert the levels from the input as we are going to compare with out the level. + // by using contains algo, so that in the reports we can add it without level, until the engine is + // updated to pass back with out level. + //ACConfig.reportLevels = mapLevelsToEngineReadableLevels(ACConfig.reportLevels); + //ACConfig.failLevels = mapLevelsToEngineReadableLevels(ACConfig.failLevels); + + // Convert the policies into a comma seperated string + ACConfig.policies = convertPolicies(ACConfig.policies); + + if (ACConfig.customRuleServer) { + constants.DEBUG && console.log("Specified Usage of custom Rule Server, switching to custom rule server"); + + // Set the ruleArchive to empty for custom rule server + ACConfig.ruleArchive = ""; + // Set the rulePack with what is provided in the configuration + ACConfig.rulePack = ACConfig.rulePack; + } else { + // In the case that baseA11yServerURL is provided in the config use that as the base otherwise switch to the default one from the constants object + var baseA11yServerURL = ACConfig.baseA11yServerURL ? ACConfig.baseA11yServerURL : constants.baseA11yServerURL; + + // Get and parse the rule archive. + var ruleArchiveFile = `${baseA11yServerURL}/archives.json`; + let ruleArchiveParse; + + constants.DEBUG && console.log("Fetching archive"); + return myrequest(ruleArchiveFile) + .then((result) => { + ruleArchiveParse = result; + constants.DEBUG && console.log("Archive file fetched:",ruleArchiveParse); + if (ruleArchiveParse && ruleArchiveParse.length > 0) { + constants.DEBUG && console.log("Found archiveFile: " + ruleArchiveFile); + ACConfig.ruleArchiveSet = ruleArchiveParse; + var ruleArchive = ACConfig.ruleArchive; + var ruleArchivePath = null; + for (var i = 0; i < ACConfig.ruleArchiveSet.length; i++) { + if (ruleArchive == ACConfig.ruleArchiveSet[i].id && !ACConfig.ruleArchiveSet[i].sunset) { + ruleArchivePath = ACConfig.ruleArchiveSet[i].path; + ACConfig.ruleArchive = ruleArchiveParse[i].name + " (" + ruleArchiveParse[i].id + ")"; + break; + } + } + if (!ruleArchivePath) { + console.log("[ERROR] RuleArchiveInvalid: Make Sure correct rule archive is provided in the configuration file. More information is available in the README.md"); + process.exit(-1); + } + //} + } else { + console.log("[ERROR] UnableToParseArchive: Archives are unable to be parse. Contact support team."); + process.exit(-1); + } + + // Build the new rulePack based of the baseA11yServerURL + ACConfig.rulePack = `${baseA11yServerURL}${ruleArchivePath}/js`; + + constants.DEBUG && console.log("Built new rulePack: " + ACConfig.rulePack); + constants.DEBUG && console.log("END 'processACConfig' function"); + + // Return the updated ACConfig object + return ACConfig; + }) + } +} + +/** + * This function is responsible initializing all the default values for the configurations, in the case any + * of the config options are missing. + * + * @param {Object} config - Provide the config object in which we need to initialize the default values. + * + * @return {Object} config - return the config object which has all the default values, in the case + * some of the options are null or undefined. + * + * @memberOf this + */ +function initializeDefaults(config) { + constants.DEBUG && console.log("START 'initializeDefaults' function"); + + constants.DEBUG && console.log("Config before initialization: "); + constants.DEBUG && console.log(config); + + // Check to make sure the rule pack server is defined, if not then set the default vaule + config.rulePack = config.rulePack || constants.rulePack; + + // Make sure all the following options are defined, otherwise reset them to default values. + config.policies = config.policies || constants.policies; + config.failLevels = config.failLevels || constants.failLevels; + config.reportLevels = config.reportLevels || constants.reportLevels; + config.outputFolder = config.outputFolder || constants.outputFolder; + config.baselineFolder = config.baselineFolder || constants.baselineFolder; + config.outputFormat = config.outputFormat || constants.outputFormat; + config.checkHiddenContent = config.checkHiddenContent || constants.checkHiddenContent; + config.extensions = config.extensions || constants.extensions; + config.engineFileName = config.engineFileName || constants.engineFileName; + config.ruleArchive = config.ruleArchive || constants.ruleArchive; + // For capture screenshots need to check for null or undefined and then set default otherwise it will evaluate the + // boolean which causes it to always comply with the default value and not user provided option + if (config.captureScreenshots === null || config.captureScreenshots === undefined || typeof config.captureScreenshots === "undefined") { + config.captureScreenshots = constants.captureScreenshots; + } + + // Load in the package.json file so that we can extract the module name and the version to build + // a toolID which needs to be used when results are build for the purpose of keeping track of + // which tool is uploading the results. + var packageObject = require('../../package.json'); + + // Build the toolID based on name and version + config.toolID = packageObject.name + "-v" + packageObject.version; + + // Using the uuid module generate a uuid number which is used to assoiciate to the scans that + // are done for a single run of karma. + config.scanID = uuid.v4(); + + constants.DEBUG && console.log("Config after initialization: "); + constants.DEBUG && console.log(config); + + constants.DEBUG && console.log("END 'initializeDefaults' function"); +} + +/** + * This function is responsible reading in the .yaml or .yml or .json and set the config options based on this. + * + * @return {Object} config - return the config object that was read in, refer to function initializeDefaults + * to view how the object is to be constructed. + * + * @memberOf this + */ +function loadConfigFromYAMLorJSONFile() { + constants.DEBUG && console.log("START 'loadConfigFromYAMLorJSONFile' function"); + + // Variable Decleration + var config = {}; + + // Get the current working directory, where we will look for the yaml, yml or json file + var workingDir = process.cwd(); + + constants.DEBUG && console.log("Working directory set to: " + workingDir); + + // List of files to look for in that order, in the case even one is found we stop and load that as the config. + // Theses files will be checked for in the working directory: + // ./.achecker.yml + // ./.achecker.yaml + // ./achecker.js + // ./achecker.json + // ./.config/.achecker.yml + // ./.config/.achecker.yaml + // ./.config/achecker.js + // ./.config/achecker.json + // The node module, require will load js or json depending on which one is present, in the case + // both json and js are present it loads js first. + // Refer to constants.js for more details + var configFiles = constants.configFiles; + + // Loop over all the possible location where the config file can reside, if one is found load it and break out. + for (var i = 0; i < configFiles.length; i++) { + + // Get the full path to the config file we are going to check + var fileToCheck = pathLib.join(workingDir, configFiles[i]); + + constants.DEBUG && console.log("Checking file: " + fileToCheck); + + // Get the extension of the file we are about to scan + var fileExtension = fileToCheck.substr(fileToCheck.lastIndexOf('.') + 1); + + // If this is a yml or yaml file verify that the file exists and then load as such. + if (fileExtension === "yml" || fileExtension === "yaml") { + // Start checking which files exists, if it exists then load it as the config that was read in + // We only allow specifying a file, and the order it checks is based on what is specified in the array. + // i.e. So in the case that it finds yml, it will load and not check the rest, etc... + if (fs.existsSync(fileToCheck)) { + + constants.DEBUG && console.log("File: " + fileToCheck + " exists loading it."); + + constants.DEBUG && console.log("Loading as YAML file."); + + // Load in as yml or yaml file and return this object + return YAML.safeLoad(fs.readFileSync(fileToCheck), 'utf8'); + } + } else { + constants.DEBUG && console.log("Trying to load as json or js."); + + // Need to use try/catch mech so that in the case the require throws an exception, we can + // catch this and discatd the error, as in the case there is no config file provided then + // we load in default values. + try { + + // call the resolve to check if the file exists or not, and get the actualy path if it was js or json + var jsOrJSONFile = require.resolve(fileToCheck); + + // Only try to load the achecker js or json file if it exists. + if (jsOrJSONFile) { + + constants.DEBUG && console.log("Loading: " + jsOrJSONFile) + + // Load in as json or js and return this object + return require(fileToCheck); + } + } catch (e) { + constants.DEBUG && console.log("JSON or JS file does not exists, will load default config.") + } + } + } + + constants.DEBUG && console.log("END 'loadConfigFromYAMLorJSONFile' function"); + + return config; +} + +/** + * This function is responsible for processing the karma configuration for accessibility-checker. + * The ACConfig provided in the Karma configuration will be processed by this + * function and then the config variables will be assoiciated to the global space so that + * they can be accessed from window.__karma__.config + * + * @param {Object} config - All the Karma configuration, we will extract what we need from this over + * all object, we need the entire object so that we can reasign some config + * variables to global scope so that all karma testscases/scripts can access + * them. + * + * @return - N/A - Object will be processed and all the params that are needed for this module will + * be extracted and then the entire object will be added to global space. + * + * @memberOf this + */ +function processConfiguration(config) { + constants.DEBUG && console.log("START 'processConfiguration' function"); + + // Variable Decleration + var ACConfig = null; + + // Read in the .yaml (.yml) or .json file to load in the configuration + let keys = [ + "customRuleServer", "rulePack", "ruleArchive", + "policies", + "failLevels", + "reportLevels", + "captureScreenshots", + "outputFormat", + "label", + "outputFolder", + "baselineFolder", + "checkHiddenContent" + ] + + let configFromFile = { } + if (typeof Cypress !== "undefined") { + keys.forEach((key) => { + configFromFile[key] = Cypress.env(key); + }) + } else { + configFromFile = loadConfigFromYAMLorJSONFile(); + } + + constants.DEBUG && console.log("Loaded config from file: "); + constants.DEBUG && console.log(configFromFile); + + // In the case configuration was provided in a yaml, yml or json file, then set this as the configuration + // otherwise load them from the Karma configuration. + if (configFromFile !== null && typeof (configFromFile) !== "undefined" && Object.keys(configFromFile).length !== 0) { + constants.DEBUG && console.log("Using config which was loaded from config file."); + + ACConfig = configFromFile; + } else { + ACConfig = {}; + } + + // In the case the ACConfig object is not defined, then define it with default config options so + // it can be set in window.__karma__.config.ACConfig, so that we know even in the testcases, other + // wrapper scripts that there was nothing defined at all, and at the same time to make sure that this + // code was actually executed. + initializeDefaults(ACConfig); + + // Now we process the final accessibility-checker config object that is build to make sure it is valid, also need to perform some + // mapping for provided paremeters to actualy read by the engine. + return processACConfig(ACConfig) + .then(() => { + // In the case the Karma config is set to config.LOG_DEBUG then also enable the accessibility-checker debuging + ACConfig.DEBUG = constants.DEBUG; + + constants.DEBUG && console.log("END 'processConfiguration' function"); + return ACConfig; + }) +} + +module.exports = processConfiguration; diff --git a/cypress-accessibility-checker/src/lib/ACConstants.js b/cypress-accessibility-checker/src/lib/ACConstants.js new file mode 100644 index 000000000..18cc7ddfb --- /dev/null +++ b/cypress-accessibility-checker/src/lib/ACConstants.js @@ -0,0 +1,92 @@ +/****************************************************************************** + Copyright:: 2020- IBM, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *****************************************************************************/ + +/******************************************************************************* + * NAME: ACConstants.js + * DESCRIPTION: Stores all the constants that are used by karma-ibma. + *******************************************************************************/ + +// Load all the modules that are needed +var pathLib = require('path'); + +// Used to specify all the constant +var constants = { + DEBUG: process.env.DEBUG === "true", + + policies: ["IBM_Accessibility"], + + // Specify the default rule pack server to use. (Where to pull the rules and engine from). + rulePack: "https://able.ibm.com/rules/archives/latest/js", + + //Specify the rule set to be use. + ruleArchive: "latest", + + // Specify default violation levels on which to fail the test. + // i.e. If specified violation then the testcase will only fail if + // a violation is found uring the scan. + failLevels: ["violation", "potentialviolation"], + + // Specify default violation levels which should be reported. + // i.e. If specified violation then in the report it would only contain + // results which are level of violation. + reportLevels: ["violation", + "potentialviolation" + ], + + // Specify default value if screenshoot should be captured of the current page that is being scanned. + captureScreenshots: false, + + // Specify default value for which type should the results be outputted to + outputFormat: "json", + + // Specify default location where the results should be saved + outputFolder: "results", + + // Specify default location where the baselines should be saved + baselineFolder: "baselines", + + // Specify default value if Hidden content be scanned or not. + checkHiddenContent: false, + + // Specify default value for Which file extensions should be checked + extensions: ["html", "htm", "svg"], + + // Specify default value for the engine file name. This the + engineFileName: "ace.js", + + // List of files to look for in that order, in the case even one is found we stop and load that as the config. + // Theses files will be checked for in the working directory: + // ./.achecker.yml + // ./.achecker.yaml + // ./achecker.js + // ./achecker.json + // ./.config/.achecker.yml + // ./.config/.achecker.yaml + // ./.config/achecker.js + // ./.config/achecker.json + // The node module, require will load js or json depending on which one is present, in the case + // both json and js are present it loads js first. + configFiles: [".achecker.yml", ".achecker.yaml", "achecker", pathLib.join(".config", ".achecker.yml"), pathLib.join(".config", ".achecker.yaml"), pathLib.join(".config", "achecker")], + + // Specify the Base Accessibility Server URL + baseA11yServerURL: "https://able.ibm.com/rules", + + // Specify true or false to allow setting rulePack with a custom server + customRuleServer: false +}; + +// Export this the constants +module.exports = constants; diff --git a/cypress-accessibility-checker/src/lib/ACTasks.js b/cypress-accessibility-checker/src/lib/ACTasks.js new file mode 100644 index 000000000..1d9ba1da5 --- /dev/null +++ b/cypress-accessibility-checker/src/lib/ACTasks.js @@ -0,0 +1,507 @@ +const ACConfigLoader = require("./ACConfigLoader"); +const ACMetricsLogger = require('./log/ACMetricsLogger'); +const ACReporterJSON = require("./reporters/ACReporterJSON"); +const ACReporterHTML = require("./reporters/ACReporterHTML"); +const ACReporterCSV = require("./reporters/ACReporterCSV"); +const DeepDiff = require("deep-diff"); + +const request = require("request"); +const fs = require("fs"); +const path = require("path"); + +let loadOnce = null; +let ace; + +const myrequest = (url) => { + if (typeof cy !== "undefined") { + return cy.request(url) + .then((data) => { + return data.body; + }) + } else { + return new Promise((resolve, reject) => { + const request = require("request"); + request.get(url, function (error, response, body) { + if (error) { + reject(error); + } else { + resolve(JSON.parse(body)); + } + }); + }); + } +} + +let loggerFunction = function (output) { + ACTasks.DEBUG && console.log(output); +}; + +let loggerCreate = function (type) { + return logger; +}; + +let logger = { + debug: loggerFunction, + info: loggerFunction, + error: loggerFunction, + warn: loggerFunction, + create: loggerCreate +}; + +let ACTasks = module.exports = { + DEBUG: false, + initializeConfig: () => { + if (ACTasks.initConfigOnce) return ACTasks.initConfigOnce; + + ACTasks.DEBUG && console.log("START 'initialize' function"); + return ACTasks.initConfigOnce = ACConfigLoader() + .then((config) => { + ACTasks.Config = config; + return config; + }); + }, + + getConfig: () => { + return ACTasks.initializeConfig(); + }, + + onRunComplete: () => { + ACTasks.reporterHTML.onRunComplete(); + ACTasks.reporterJSON.onRunComplete(); + ACTasks.reporterCSV.onRunComplete(); + return true; + }, + + initialize: () => { + if (ACTasks.initOnce) { + return ACTasks.initOnce.then(() => { + if (!ACTasks.ace) { + ACTasks.initOnce = false; + return ACTasks.initialize(); + } + }) + } + + return ACTasks.initOnce = ACTasks.initializeConfig().then(() => { + if (ACTasks.Config.rulePack.includes("localhost")) { + process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; + } + + ACTasks.reporterHTML = new ACReporterHTML(ACTasks); + ACTasks.reporterJSON = new ACReporterJSON(ACTasks); + ACTasks.reporterCSV = new ACReporterCSV(ACTasks); + + // Specify if debug information should be printed or not + ACTasks.DEBUG = ACTasks.Config.DEBUG; + + // Array that contains the list of entries that need to be compared between the actual and baseline objects only. + // Note: This is used by the cleanComplianceObjectBeforeCompare function to filter the report based on this. + ACTasks.baselineIssueList = ["ruleId", "xpath"]; + ACTasks.metricsLogger = new ACMetricsLogger("cypress-accessibility-checker", logger, ACTasks.Config.policies.join(",")); + + // Initialize the scanSummary object with summary information for accessibility-checker + ACTasks.scanSummary = ACTasks.initializeSummary(); + + // Initialize the global object which will store all the diff results for a scan that is run, using + // actual and expected. + ACTasks.diffResults = {}; + + // Initialize the global object which will store all the scan results indexed by the label. + ACTasks.scanResults = {}; + + ACTasks.engineLoaded = false; + return ACTasks.loadEngine() + }) + .then(() => { + ACTasks.DEBUG && console.log("END 'initialize' function"); + }) + }, + initializeSummary: () => { + // Variable Decleration + let scanSummary = {}; + let reportLevels = ACTasks.Config.reportLevels; + + // Initialize counts + scanSummary.counts = {}; + + // In the case that report levels are provided then populate the count object in + // scanSummary.counts object with the levels which were provided in reportLevels + // array. + if (reportLevels) { + + // Iterate over the report levels and populate the pageResultsWithCount counts + // object + reportLevels.forEach(function (levels) { + scanSummary.counts[levels] = 0; + }); + scanSummary.counts.ignored = 0; + } + // Populate the scanSummary.counts object with all the levels + else { + scanSummary.counts = { + "violation": 0, + "potentialviolation": 0, + "recommendation": 0, + "potentialrecommendation": 0, + "manual": 0, + "pass": 0 + }; + } + + // Add Start time when this script is loaded into browser + // Start time will be in milliseconds elapsed since 1 January 1970 00:00:00 UTC up until now. + scanSummary.startReport = Date.now(); + + // Leave end report as empty for now + scanSummary.endReport = ''; + + // Add the toolID, policies, reportLevels, failLevels and labels from the config to the summary + scanSummary.toolID = ACTasks.Config.toolID; + scanSummary.policies = ACTasks.Config.policies.join(","); + scanSummary.reportLevels = ACTasks.Config.reportLevels; + scanSummary.labels = ACTasks.Config.label; + scanSummary.failLevels = ACTasks.Config.failLevels; + + // Add scanID (UUID) to the scan summary object + scanSummary.scanID = ACTasks.Config.scanID; + + // Build the paceScanSummary object which will contains all the items that were scanned and a count + // summary for each of the scanned items. + scanSummary.pageScanSummary = []; + + return scanSummary; + }, + /** + * This function loads the compliance engine. + * @param {Function} callback - Provide callback function which will be executed once the engine is loaded + * + * @return N/A - This function will not return any thing, as it is full async + */ + loadEngine: (content) => { + ACTasks.DEBUG && console.log("START 'aChecker.loadEngine' function"); + + if (typeof cy === "undefined") { + // We aren't loading the engine in tasks outside of the cypress browser engine + if (ace) { + return Promise.resolve(ace); + } else { + return ACTasks.loadLocalEngine((engine) => { + return ACTasks.ace = ace = engine; + }); + } + } + }, + loadLocalEngine: function () { + if (loadOnce === null) { + loadOnce = ACTasks.getConfig() + .then((config) => { + return new Promise((resolve, reject) => { + request.get(config.rulePack + "/ace-node.js", function (err, data) { + err && console.error(err); + if (!data) { + console.log("Cannot read: " + ACTasks.Config.rulePack + "/ace-node.js"); + } + data = data.body; + let engineDir = path.join(__dirname, "engine"); + if (!fs.existsSync(engineDir)) { + fs.mkdirSync(engineDir); + } + let cacheDir = path.join(engineDir, "cache"); + if (!fs.existsSync(cacheDir)) { + fs.mkdirSync(cacheDir); + } + let engineFilename = path.join(engineDir, "ace-node.js"); + // Only write the engine if it's different - can cause Cypress to trigger a file changed watch + if (fs.existsSync(engineFilename)) { + if (fs.readFileSync(engineFilename).toString() === data) { + try { + err && console.log(err); + ACTasks.ace = require("./engine/ace-node"); + return resolve(ACTasks.ace); + } catch (e) { + console.log(e); + return reject(e); + } + } + } + fs.writeFile(path.join(engineDir, "ace-node.js"), data, function (err) { + try { + err && console.log(err); + ACTasks.ace = require("./engine/ace-node"); + } catch (e) { + console.log(e); + return reject(e); + } + resolve(ace); + }); + }); + }); + }); + } + return loadOnce; + }, + /** + * This function is responsible for sending the scan results to the karma server accessibility-checker reporter. The + * accessibility-checker reporter is responsible for writing the results to a file. The reporter will also keep track of + * the summary results, on the server side. + * + * @param {Object} results - Provide the full results object which is to be reported/saved to file. + * refer to return in function "aChecker.buildReport" prolog + * + * @return N/A + * + * PRIVATE METHOD + * + * @memberOf this + */ + sendResultsToReporter: function (unFilteredResults, results, profile) { + return ACTasks.initialize().then(() => { + ACTasks.DEBUG && console.log("sendResultsToReporter:", ACTasks.Config.outputFormat); + if (ACTasks.Config.outputFormat.indexOf("json") != -1) { + ACTasks.reporterJSON.report(results); + } + if (ACTasks.Config.outputFormat.includes("csv")) { + ACTasks.reporterCSV.report(results); + } + if (ACTasks.Config.outputFormat.indexOf("html") != -1) { + ACTasks.reporterHTML.report(unFilteredResults); + } + // Only perform the profiling if profiling was not disabled on purpose + if (!ACTasks.Config.label || ACTasks.Config.label.indexOf("IBMa-Node-TeSt") === -1) { + // Meter the usage here + ACTasks.metricsLogger.profileV2(results.summary.scanTime, profile); + } + return 0; + }); + }, + + loadBaselines: (parentDir, parResult) => ACTasks.initializeConfig().then(() => new Promise((resolve, reject) => { + let result = parResult || {} + let readDirPath = path.join(process.cwd(), ACTasks.Config.baselineFolder); + if (parentDir) { + readDirPath += parentDir; + } + fs.readdir(readDirPath, function (err, files) { + //handling error + if (err) { + return console.log('Unable to scan directory: ' + err); + } + for (let file of files) { + let filePath = path.join(readDirPath,file); + file = file.split('.').slice(0, -1).join('.') + + if (fs.lstatSync(filePath).isFile()) { + if (parentDir) { + result[parentDir.substring(1)+"/"+file] = require(filePath); + } else { + result[file] = require(filePath); + } + } else if (fs.lstatSync(filePath).isDirectory()) { + ACTasks.loadBaselines((parentDir || "")+"/"+file, result); + } + }; + return resolve(result); + }); + })), + /** + * This function is responsible for getting the baseline object for a label that was provided. + * + * @param {String} label - Provide a lable for which to get the baseline for. + * + * @return {Object} - return the baseline object from global space based on label provided, the object will be + * in the same format as outlined in the return of aChecker.buildReport function. + * + * PUBLIC API + * + * @memberOf this + */ + getBaseline: function (label) { + try { + return require(path.join(path.join(process.cwd(), ACTasks.Config.baselineFolder), label)); + } catch (e) { + return null; + } + }, + + getRulesets: () => new ACTasks.ace.Checker().rulesets, + + /** + * This function is responsible for printing the scan results to console. + * + * @param {Object} results - Provide the results from the scan. + * + * @return {String} resultsString - String representation of the results/violations. + * + * PUBLIC API + * + * @memberOf this + */ + stringifyResults: function (report) { + return ACTasks.initialize() + .then(() => { + // console.log(report); + // Variable Decleration + let resultsString = `Scan: ${report.label}\n`; + + // Loop over the reports and build the string version of the the issues within each report + report.results && report.results.forEach(function (issue) { + if (ACTasks.Config.reportLevels.includes(issue.level)) { + // Build string of the issues with only level, messageCode, xpath and snippet. + resultsString += "- Message: " + issue.message + + "\n Level: " + issue.level + + "\n XPath: " + issue.path.dom + + "\n Snippet: " + issue.snippet + + "\n"; + } + }); + + return resultsString; + }); + }, + + getHelpURL: function (ruleId) { + return new ACTasks.ace.Checker().engine.getHelp(ruleId); + }, + + /** + * This function is responsible for comparing actual with expected and returning all the differences as an array. + * + * @param {Object} actual - Provide the actual object to be used for compare + * @param {Object} expected - Provide the expected object to be used for compare + * @param {boolean} clean - Provide a boolean if both the actual and expected objects need to be cleaned + * cleaning refers to converting the objects to match with a basic compliance + * compare of xpath and ruleId. + * + * @return {Object} differences - return an array of diff objects that were found, following is the format of the object: + * [ + * { + * "kind": "E", + * "path": [ + * "reports", + * 0, + * "issues", + * 10, + * "xpath" + * ], + * "lhs": "/html[1]/body[1]/div[2]/table[5]", + * "rhs": "/html[1]/body[1]/div[2]/table[5]d", + * }, + * { + * "kind": "E", + * "path": [ + * "label" + * ], + * "lhs": "Table-layoutMultiple", + * "rhs": "dependencies/tools-rules-html/v2/a11y/test/g471/Table-layoutMultiple.html", + * } + * ] + * + * PUBLIC API + * + * @memberOf this + */ + diffResultsWithExpected: function (actual, expected, clean) { + return ACTasks.initialize().then(() => { + // In the case clean is set to true then run the cleanComplianceObjectBeforeCompare function on + // both the actual and expected objects passed in. This is to make sure that the objcet follow a + // simalar structure before compareing the objects. + if (clean) { + // Clean actual and expected objects + actual = ACTasks.cleanComplianceObjectBeforeCompare(actual); + expected = ACTasks.cleanComplianceObjectBeforeCompare(expected); + } + + // Run Deep diff function to compare the actual and expected values. + let differences = DeepDiff.diff(actual, expected); + + // Return the results of the diff, which will include the differences between the objects + if (!differences) return null; + + return differences; + }) + }, + + /** + * This function is responsible for cleaning up the compliance baseline or actual results, based on + * a pre-defined set of criterias, such as the following: + * 1. No need to compare summary object + * 2. Only need to compare the ruleId and xpath in for each of the issues + * + * @param {Object} objectToClean - Provide either an baseline or actual results object which would be in the + * the same format as outlined in the return of aChecker.buildReport function. + * + * @return {Object} objectToClean - return an object that was cleaned to only contain the information that is + * needed for compare. Following is a sample of how the cleaned object will look like: + * { + * "label": "unitTestContent", + * "reports": [ + * { + * "frameIdx": "0", + * "frameTitle": "Frame 0", + * "issues": [ + * { + * "ruleId": "1", + * "xpath": "/html[1]/head[1]/style[1]" + * } + * .... + * ] + * }, + * { + * "frameIdx": "1", + * "frameTitle": "Frame 1", + * "issues": [ + * { + * "ruleId": "471", + * "xpath": "/html[1]/body[1]/div[2]/table[3]" + * } + * .... + * ] + * } + * ] + * } + * + * PRIVATE METHOD + * + * @memberOf this + */ + cleanComplianceObjectBeforeCompare: function (objectToClean) { + // Clone the object so that we do not reference the original or else it causes the original + // results object or baseline object to get updated, which we do not want as users are allowed + // access to the raw results object and baseline object. + // Convert the object into string and then parse it as a JSON object which will lose its reference + objectToClean = JSON.parse(JSON.stringify(objectToClean)); + + // Remove the summary object, scanID, toolID, issueMessage + delete objectToClean.summary; + delete objectToClean.nls; + delete objectToClean.scanID; + delete objectToClean.toolID; + delete objectToClean.issueMessages; + delete objectToClean.numExecuted; + + + // Loop over all the issues and remove the keys that are not needed for the compare + // Only leave the ruleId and xpath keys for compare. + for (let idx = 0; idx < objectToClean.results.length; ++idx) { + const issue = objectToClean.results[idx]; + if (issue.level === "pass") { + objectToClean.results.splice(idx--, 1); + } else { + issue.xpath = issue.path.dom; + // Loop over all the keys in a single issue object and remove all the + // keys that are not needed for compare + Object.keys(issue).forEach(function (key) { + // Remove all the keys which are not in the baselineIssueList + if (ACTasks.baselineIssueList.indexOf(key) === -1) { + delete issue[key]; + } + }); + // Make sure that the xpath in the case there is a [1] we replace it with "" + // to support some browser which return it differently + issue.xpath = issue.xpath.replace(/\[1\]/g, ""); + } + }; + + return objectToClean; + } +} \ No newline at end of file diff --git a/cypress-accessibility-checker/src/lib/log/ACMetricsLogger.js b/cypress-accessibility-checker/src/lib/log/ACMetricsLogger.js new file mode 100644 index 000000000..a0433542a --- /dev/null +++ b/cypress-accessibility-checker/src/lib/log/ACMetricsLogger.js @@ -0,0 +1,176 @@ +/****************************************************************************** + Copyright:: 2020- IBM, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *****************************************************************************/ + +/******************************************************************************* + * NAME: ACMetricsLogger.js + * DESCRIPTION: Common Metrics logger object which can be shared between tools + * to upload metrics of the tool to the metrics server. + *******************************************************************************/ + +// Load required modules +var request = require('request'); + +/** + * This function is responsible for constructing the accessibility-checker Metrics object which contains all the function + * that are needed to upload scan metrics to the metric server. + * + * @param {String} toolName - The name of the tool sending the metrics. + * @param {String} logger - Logger object which can be used to log debug information. + * @param {String} policies - Array of policies which will be sent to the metrics server. + * + * @return - N/A + * + * @memberOf this + */ +var ACMetricsLogger = function (toolName, logger, policies) { + try { + // Variable Decleration + this.metricsURLV2 = "https://able.ibm.com/tools"; + + // accessibility-checker Metrics Logger + this.log = logger; + + // Init all the local object variables + this.toolName = toolName; + + // Contains the time it took for a scan for the V1 metric server + this.scanTimesV1 = []; + + // Contains the scan times indexed by profile for V2 metric server + this.scanTimesV2 = {}; + + // Additional details to upload to the metrics server + this.policies = policies; + + // In the case that policies provided is an array convert it to a comma seperated list + if (this.policies instanceof Array) { + this.policies = this.policies.join(","); + } + + /** + * This function is responsible for profiling the testcases and adding the scan time to the global + * array which will be sent to the metrics server to log the number of scans that were performed. + * This function profiles scanTimes for the V2 metric server: + * https://aat.w3ibm.mybluemix.net + * + * In the case that user provides any url that is https://aat* it will upload based on accountId + * + * @param {String} scanTime - Provide the time it took for the testcase to run + * @param {String} profile - The type of profile the scan time is for: + * i.e. browser information, features, etc... + * + * @return N/A - Global scanTimesV2 object is updated with the time + * + * @memberOf this + */ + this.profileV2 = function (scanTime, profile) { + this.log.debug("START 'profileV2' function"); + + // URI encode the profile text provided + profile = encodeURIComponent(profile); + + // Add the time it took for the testcase to run to the global array, indexed by the profile + this.scanTimesV2[profile] = this.scanTimesV2[profile] || []; + this.scanTimesV2[profile].push(scanTime); + + this.log.debug("END 'profileV2' function"); + }; + + /** + * This function is responsible for uploading scan results to the metrics server: + * https://aat.w3ibm.mybluemix.net + * + * @param {Function} done - The browser on which the testcases were run on + * + * @return N/A - performs the upload of the metrics to the server + * + * @memberOf this + */ + this.sendLogsV2 = function (done) { + this.log.debug("START 'sendLogsV2' function"); + + // Copy this.log into loggerInScope so that it can be used in callback function + var loggerInScope = this.log; + + try { + // Variable Decleration + var numProfiles = 0; + var accountId = ""; + + // Reset the timeout to 0 + if (this.timeout) { + this.timeout(0); + } + + // Loop over all the profiles with in the scanTime Object + for (var profile in this.scanTimesV2) { + + // Loop over all the V2 Scan Times until it reaches 0 + while (this.scanTimesV2[profile].length > 0) { + // Build a truncatedScanTime Array to upload to the metrics server chunck by chunck + var subScanTimes = this.scanTimesV2[profile].splice(0, 150); + + // Increment the num Profile + ++numProfiles; + + // Start building the Query string to be sent to the metrics server + var qs = "?t=" + this.toolName + "&tag=" + profile + "&a=" + accountId + "&pol=" + this.policies + "&st="; + + subScanTimes.forEach(function (t) { + qs += t; + qs += ","; + }); + qs = qs.substr(0, qs.length - 1); + + this.log.debug("Uploading: " + this.metricsURLV2 + "/api/pub/meter/v2" + qs); + + // Dispatch the call to the metrics server + // Istanbul is not able to capture the coverate of functions call in a callback therefore we need to skip + /* istanbul ignore next */ + request.get(this.metricsURLV2 + "/api/pub/meter/v2" + qs, function () { + // Decrement the numProfiles to identify that scan has finished + --numProfiles; + + // Once all metrics for all profiles have been uploaded we end this function call + if (numProfiles === 0) { + loggerInScope.debug("END 'sendLogsV2' function"); + done && done(); + } + }); + } + } + + // Once all metrics for all profiles have been uploaded we end this function call + if (numProfiles === 0) { + this.log.debug("END 'sendLogsV2' function"); + done && done(); + } + } catch (e) { + /* istanbul ignore next */ + this.log.debug("Error uploading metrics logs: " + e); + /* istanbul ignore next */ + this.log.debug("END 'sendLogsV2' function"); + /* istanbul ignore next */ + done && done(); + } + }; + } catch (e) { + console.error(e); + } +}; + +// Export this function, which can be used to create a new metrics logger object +module.exports = ACMetricsLogger; diff --git a/cypress-accessibility-checker/src/lib/reporters/ACReporterCSV.js b/cypress-accessibility-checker/src/lib/reporters/ACReporterCSV.js new file mode 100644 index 000000000..a25605015 --- /dev/null +++ b/cypress-accessibility-checker/src/lib/reporters/ACReporterCSV.js @@ -0,0 +1,263 @@ +/****************************************************************************** + Copyright:: 2020- IBM, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *****************************************************************************/ + +// Load all the modules that are needed +var pathLib = require('path'); +var fs = require('fs'); +const path = require("path"); +// Variable Declearation +var log; + +// Global aChecker Summary Holder +var scanSummary = {}; + +/** + * This function is responsible for constructing the aChecker Reporter which will be used to, report + * the scan results, such as writing the page results and the summary to a JSON file. This reporter function + * is registered with Karma server and triggered based on events that are triggered by the karma communication. + * + * @param {Object} baseReporterDecorator - the base karma reporter, which had the base functions which we override + * @param {Object} config - All the Karma configuration, we will extract what we need from this over + * all object, we need the entire object so that we detect any changes in the object + * as other plugins are loaded and the object is updated dynamically. + * @param {Object} logger - logger object which is used to log debug/error/info messages + * @param {Object} emitter - emitter object which allows to listem on any event that is triggered and allow to execute + * custom code, to handle the event that was triggered. + * + * @return - N/A + * + * @memberOf this + */ +var ACReporter = function (aChecker) { + let resultStr = ""; + let Config = aChecker.Config; + Config.DEBUG && console.log("START ACReporter Constructor"); + // Override adapters + this.adapters = []; + + // This emitter function is responsible for calling this function when the info event is detected + this.report = function(info) { + Config.DEBUG && console.log("START 'info' emitter function"); + + // Save the results of a single scan to a JSON file based on the label provided + savePageResults(info); + + // Update the overall summary object count object to include the new scan that was performed + addToSummaryCount(info.summary.counts); + + // Save the summary of this scan into global space of this reporter, to be logged + // once the whole scan is done. + addResultsToGlobal(info); + + Config.DEBUG && console.log("END 'info' emitter function"); + }; + + /** + * This function is responsible for performing any action when the entire karma run is done. + * Overrides onRunComplete function from baseReporterDecorator + * + * @override + * + * @memberOf this + */ + this.onRunComplete = function () { + Config.DEBUG && console.log("START 'ACReporterCSV::onRunComplete' function"); + + // Add End time when the whole karma run is done + // End time will be in milliseconds elapsed since 1 January 1970 00:00:00 UTC up until now. + scanSummary.endReport = Date.now(); + + // Save summary object to a JSON file. + saveSummary(scanSummary); + + Config.DEBUG && console.log("END 'ACReporterCSV::onRunComplete' function"); + }; + + var toCSV = function(str) { + if (str === null) { + return '"null"'; + } else if (str.length == 0) { + return '""'; + } else { + str = str.replace(/"/g, '""'); + return `"${str}"`; + } + } + /** + * This function is responsible for saving a single scans results to a file as JSON. On a side note + * this function will also extract the label which will be the file names where the results will be + * saved. + * + * @param {Object} config - Karma config object, used to extrat the outputFolder from the ACConfig. + * @param {Object} results - Provide the scan results for a single page that should be saved. + * + * @memberOf this + */ + var savePageResults = function (report) { + Config.DEBUG && console.log("START 'savePageResults' function"); + + for (const result of report.results) { + resultStr += `${toCSV(report.label)},${toCSV(result.level)},${toCSV(result.ruleId)},${toCSV(result.message)},${toCSV(result.path.dom)},${toCSV(aChecker.getHelpURL(result.ruleId))}\n` + } + + Config.DEBUG && console.log("END 'savePageResults' function"); + } + + /** + * This function is responsible for converting a javascript object into JSON and then writing that to a + * json file. + * + * @param {String} fileName - Full path of file where the JSON object should be stored + * @param {String} content - The javascript object which should be converted and saved to file as JSON. + * + * @memberOf this + */ + var writeObjectToFile = function (fileName, content) { + Config.DEBUG && console.log("START 'writeObjectToFileAsCSV' function"); + fileName = path.join(aChecker.Config.outputFolder, fileName); + // Extract the parent directory of the file name that is provided + var parentDir = pathLib.dirname(fileName); + + Config.DEBUG && console.log("Parent Directoy: \"" + parentDir + "\""); + + // In the case that the parent directoy does not exist, create the directories + if (!fs.existsSync(parentDir)) { + + // Create the parent directory recerseivly if it does not exist. + fs.mkdirSync(parentDir, { recursive: true}); + } + + Config.DEBUG && console.log("Object will be written to file: \"" + fileName + "\""); + + // Convert the Object into JSON string and write that to the file + // Make sure to use utf-8 encoding to avoid an issues specify to OS. + // In terms of the JSON string that is constructed use 4 spaces to format the JSON object, before + // writing it to the file. + fs.writeFileSync(fileName, content, { encoding: 'utf-8' }); + + Config.DEBUG && console.log("END 'writeObjectToFileAsCSV' function"); + } + + /** + * This function is responsible for initializing the summary object which will store all informations about the + * scans that will occurs while karma is still running and running compliance scans. + * + * @return {Object} scanSummary - return the built scan summary object, which will follow the following format: + * { + * "scanID": "ef3aec68-f073-4f9c-b372-421ae00bd55d", + * "counts": { + * "violation": 0, + * "potentialviolation": 0, + * "recommendation": 0, + * "potentialrecommendation": 0, + * "manual": 0 + * }, + * "startReport": "2016-06-06T00:52:41.603Z", + * "endReport": "", + * "toolID": "karma-ibma-v1.0.0", + * "policies": [ + * "CI162_5_2_DCP070116", + * "CI162_5_2_DCP080115" + * ], + * "reportLevels": [ + * "violation", + * "potentialviolation", + * "recommendation", + * "potentialrecommendation", + * "manual" + * ], + * "labels": [ + * "Firefox", + * "master", + * "V12", + * "Linux" + * ], + * "pageScanSummary": {} + * } + * + * @memberOf this + */ + var initializeSummary = function (config) { + resultStr = `Label,Level,RuleId,Message,Xpath,Help\n` + return scanSummary; + } + + /** + * This function is responsible for saving the summary object of the while scan to a summary file. + * + * @param {Object} summary - The summary object that needs to be written to the summary file. + * + * @memberOf this + */ + var saveSummary = function (summary, done) { + if (Config.outputFormat.indexOf("csv") === -1) { + return; + } + Config.DEBUG && console.log("START 'saveSummary' function"); + writeObjectToFile("results.csv", resultStr); + Config.DEBUG && console.log("END 'saveSummary' function"); + } + + /** + * This function is responsible for indexing the results into global spaces based on label. + * + * @param {Object} results - Results object which will be provided to the user/wroten to the file. + * Refer to aChecker.buildReport function's return to figure out what the object + * will look like. + * + * @return - N/A - Global object is updated with the results + * + * @memberOf this + */ + var addResultsToGlobal = function (results) { + Config.DEBUG && console.log("START 'addResultsToGlobal' function"); + Config.DEBUG && console.log("END 'addResultsToGlobal' function"); + } + + /** + * This function is responsible for updating/creating the global violation summary for the engine karma run + * for browser that it is running on. Will take the pageCount object which is part of the page object and + * add extract the values for each of the levels and add them to the global object. This will provide an overall + * summary of violations for all testcases run and all scans done. + * + * @param {Object} pageCount - Provide the page count object, in the following format: + * + * @return N/A - Global summary object is updated with the counts + * + * @memberOf this + */ + var addToSummaryCount = function (pageCount) { + + } + + scanSummary = initializeSummary(); + + var myThis = this; + if (typeof(after) !== "undefined" && typeof cy === "undefined") { + after(function(done) { + myThis.onRunComplete(); + done && done(); + }); + } else { + // process.on('beforeExit', function() { + // myThis.onRunComplete(); + // }); + } + Config.DEBUG && console.log("END ACReporter Constructor"); +}; + +// Export this function, which will be called when Karma loads the reporter +module.exports = ACReporter; diff --git a/cypress-accessibility-checker/src/lib/reporters/ACReporterHTML.js b/cypress-accessibility-checker/src/lib/reporters/ACReporterHTML.js new file mode 100644 index 000000000..5239491df --- /dev/null +++ b/cypress-accessibility-checker/src/lib/reporters/ACReporterHTML.js @@ -0,0 +1,422 @@ +/****************************************************************************** + Copyright:: 2020- IBM, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *****************************************************************************/ + +// Load all the modules that are needed +var pathLib = require('path'); +var fs = require('fs'); +require("../ACConfigLoader"); +const { genReport } = require("./genReport"); + +// Global aChecker Summary Holder +var scanSummary = {}; + +/** + * This function is responsible for constructing the aChecker Reporter which will be used to, report + * the scan results, such as writing the page results and the summary to a HTML file. This reporter function + * is registered with Karma server and triggered based on events that are triggered by the karma communication. + * + * @param {Object} baseReporterDecorator - the base karma reporter, which had the base functions which we override + * @param {Object} config - All the Karma configuration, we will extract what we need from this over + * all object, we need the entire object so that we detect any changes in the object + * as other plugins are loaded and the object is updated dynamically. + * @param {Object} logger - logger object which is used to log debug/error/info messages + * @param {Object} emitter - emitter object which allows to listem on any event that is triggered and allow to execute + * custom code, to handle the event that was triggered. + * + * @return - N/A + * + * @memberOf this + */ +var ACReporterHTML = function (aChecker) { + let Config = aChecker.Config; + Config.DEBUG && console.log("START ACReporterHTML Constructor"); + // Override adapters + this.adapters = []; + + // This emitter function is responsible for calling this function when the info event is detected + this.report = function(info) { + Config.DEBUG && console.log("START 'info' emitter function"); + + // Save the results of a single scan to a HTML file based on the label provided + savePageResults(info); + + // Update the overall summary object count object to include the new scan that was performed + addToSummaryCount(info.summary.counts); + + // Save the summary of this scan into global space of this reporter, to be logged + // once the whole scan is done. + addResultsToGlobal(info); + + Config.DEBUG && console.log("END 'info' emitter function"); + }; + + /** + * This function is responsible for performing any action when the entire karma run is done. + * Overrides onRunComplete function from baseReporterDecorator + * + * @override + * + * @memberOf this + */ + this.onRunComplete = function () { + Config.DEBUG && console.log("START 'ACReporterHTML:onRunComplete' function"); + + // Add End time when the whole karma run is done + // End time will be in milliseconds elapsed since 1 January 1970 00:00:00 UTC up until now. + scanSummary.endReport = Date.now(); + + // Save summary object to a HTML file. + saveSummary(scanSummary); + + Config.DEBUG && console.log("END 'ACReporterHTML:onRunComplete' function"); + }; + + /** + * This function is responsible for saving a single scans results to a file as HTML. On a side note + * this function will also extract the label which will be the file names where the results will be + * saved. + * + * @param {Object} config - Karma config object, used to extrat the outputFolder from the ACConfig. + * @param {Object} results - Provide the scan results for a single page that should be saved. + * + * @memberOf this + */ + var savePageResults = function (results) { + Config.DEBUG && console.log("START 'ACReporterHTML:savePageResults' function"); + + // Extract the outputFolder from the ACConfig (this is the user config that they provid) + var resultDir = Config.outputFolder; + + Config.DEBUG && console.log("Results are going to be stored under results directory: \"" + resultDir + "\""); + + // Build the full file name based on the label provide in the results and also the results dir specified in the + // configuration. + var resultsFileName = pathLib.join(resultDir, results.label + '.html'); + + /**************************************************** DEBUG INFORMATION *************************************************************** + // Debug example which has label which has unix "/" in them. + var resultsFileName = pathLib.join(resultDir, "dependencies/tools-rules-html/v2/a11y/test/g471/Table-layoutMultiple.html.json"); + + // Debug example which has a label which has Windows "\" in them. + var resultsFileName = pathLib.join(resultDir, "dependencies\\tools-rules-html\\v2\\a11y\\test\\g471\\Table-layoutMultiple.html.json"); + ***************************************************************************************************************************************/ + + // Write the results object as HTML to a file. + writeObjectToFileAsHTML(resultsFileName, results); + + Config.DEBUG && console.log("END 'ACReporterHTML:savePageResults' function"); + } + + /** + * This function is responsible for converting a javascript object into HTML and then writing that to a + * json file. + * + * @param {String} fileName - Full path of file where the HTML object should be stored + * @param {String} content - The javascript object which should be converted and saved to file as HTML. + * + * @memberOf this + */ + var writeObjectToFileAsHTML = function (fileName, content) { + const valueMap = { + "VIOLATION": { + "POTENTIAL": "Needs review", + "FAIL": "Violation", + "PASS": "Pass", + "MANUAL": "Recommendation" + }, + "RECOMMENDATION": { + "POTENTIAL": "Recommendation", + "FAIL": "Recommendation", + "PASS": "Pass", + "MANUAL": "Recommendation" + } + }; + + Config.DEBUG && console.log("START 'ACReporterHTML:writeObjectToFileAsHTML' function"); + + // Extract the parent directory of the file name that is provided + var parentDir = pathLib.dirname(fileName); + + Config.DEBUG && console.log("Parent Directoy: \"" + parentDir + "\""); + + // In the case that the parent directoy does not exist, create the directories + if (!fs.existsSync(parentDir)) { + + // Create the parent directory recerseivly if it does not exist. + fs.mkdirSync(parentDir, { recursive: true}); + } + + Config.DEBUG && console.log("Object will be written to file: \"" + fileName + "\""); + + let outReport = { + report: { + timestamp: content.summary.startScan, + nls: content.nls, + results: content.results, + counts: { + total: { } + } + }, + rulesets: aChecker.getRulesets(), + tabURL: content.summary.URL + } + outReport.report.counts.total.All = 0; + for (const item of content.results) { + let val = valueMap[item.value[0]][item.value[1]] || item.value[0] + "_" + item.value[1]; + outReport.report.counts.total[val] = (outReport.report.counts.total[val] || 0) + 1; + ++outReport.report.counts.total.All; + } + + // Convert the Object into HTML string and write that to the file + // Make sure to use utf-8 encoding to avoid an issues specify to OS. + // In terms of the HTML string that is constructed use 4 spaces to format the HTML object, before + // writing it to the file. + fs.writeFileSync(fileName, genReport(outReport), { encoding: 'utf-8' }); + + Config.DEBUG && console.log("END 'ACReporterHTML:writeObjectToFileAsHTML' function"); + } + + /** + * This function is responsible for initializing the summary object which will store all informations about the + * scans that will occurs while karma is still running and running compliance scans. + * + * @return {Object} scanSummary - return the built scan summary object, which will follow the following format: + * { + * "scanID": "ef3aec68-f073-4f9c-b372-421ae00bd55d", + * "counts": { + * "violation": 0, + * "potentialviolation": 0, + * "recommendation": 0, + * "potentialrecommendation": 0, + * "manual": 0 + * }, + * "startReport": "2016-06-06T00:52:41.603Z", + * "endReport": "", + * "toolID": "karma-ibma-v1.0.0", + * "policies": [ + * "CI162_5_2_DCP070116", + * "CI162_5_2_DCP080115" + * ], + * "reportLevels": [ + * "violation", + * "potentialviolation", + * "recommendation", + * "potentialrecommendation", + * "manual" + * ], + * "labels": [ + * "Firefox", + * "master", + * "V12", + * "Linux" + * ], + * "pageScanSummary": {} + * } + * + * @memberOf this + */ + var initializeSummary = function (config) { + // Variable Decleration + var scanSummary = {}; + var reportLevels = Config.reportLevels; + + // Initialize counts + scanSummary.counts = {} + + // In the case that report levels are provided then populate the count object in + // scanSummary.counts object with the levels which were provided in reportLevels + // array. + if (reportLevels) { + + // Iterate over the report levels and populate the pageResultsWithCount counts + // object + reportLevels.forEach(function (levels) { + scanSummary.counts[levels] = 0; + }); + } + // Populate the scanSummary.counts object with all the levels + else { + scanSummary.counts = { + "violation": 0, + "potentialviolation": 0, + "recommendation": 0, + "potentialrecommendation": 0, + "manual": 0, + "pass": 0 + }; + } + + // Add ignored count disableIgnore field in config file is not true + if(Config.disableIgnore == undefined || Config.disableIgnore == false || Config.disableIgnore == null){ + scanSummary.counts.ignored = 0; + } + + // Add Start time when this script is loaded into browser + // Start time will be in milliseconds elapsed since 1 January 1970 00:00:00 UTC up until now. + scanSummary.startReport = Date.now(); + + // Leave end report as empty for now + scanSummary.endReport = ''; + + // Add the toolID, ruleArchive, policies, reportLevels, failLevels and labels from the config to the summary + scanSummary.toolID = Config.toolID; + // Append the id and the name of the archive to the report. + scanSummary.policies = Config.policies; + scanSummary.ruleArchive = Config.ruleArchive; + scanSummary.reportLevels = Config.reportLevels; + scanSummary.labels = Config.label; + scanSummary.failLevels = Config.failLevels; + + // Add scanID (UUID) to the scan summary object + scanSummary.scanID = Config.scanID; + + // Build the paceScanSummary object which will contains all the items that were scanned and a count + // summary for each of the scanned items. + scanSummary.pageScanSummary = []; + + return scanSummary; + } + + /** + * This function is responsible for saving the summary object of the while scan to a summary file. + * + * @param {Object} summary - The summary object that needs to be written to the summary file. + * + * @memberOf this + */ + var saveSummary = function (summary) { + + } + + /** + * This function is responsible for checking if a number needs a leading 0, and if it is needed + * add the leading 0 and return that as the new number. + * + * @param {int} number - Provide a number to check if a leading 0 needs to be added or not. + * + * @return {String} number - Return the number with the leading 0 added back + * + * @memberOf this + */ + var datePadding = function(number) { + Config.DEBUG && console.log("START 'datePadding' function"); + + // In the case that the number is less then 10 we need to add the leading '0' to the number. + number = number < 10 ? '0' + number : number; + + Config.DEBUG && console.log("END 'datePadding' function"); + + return number; + } + + /** + * This function is responsible for indexing the results into global spaces based on label. + * + * @param {Object} results - Results object which will be provided to the user/wroten to the file. + * Refer to aChecker.buildReport function's return to figure out what the object + * will look like. + * + * @return - N/A - Global object is updated with the results + * + * @memberOf this + */ + var addResultsToGlobal = function (results) { + Config.DEBUG && console.log("START 'addResultsToGlobal' function"); + + // Build the single page summary object to follow the following format: + // "label": "dependencies/tools-rules-html/v2/a11y/test/g471/Table-DataNoSummaryARIA.html", + // "counts": { + // "violation": 1, + // "potentialviolation": 0, + // "recommendation": 0, + // "potentialrecommendation": 0, + // "manual": 0 + // } + var pageSummaryObject = { + label: results.label, + counts: results.summary.counts + } + + Config.DEBUG && console.log("Adding following object to scanSummary.pageScanSummary: "); + Config.DEBUG && console.log(pageSummaryObject); + + // Add the summary count for this scan to the pageScanSummary object which is in the global space + // Index this by the label. + scanSummary.pageScanSummary.push(pageSummaryObject); + + Config.DEBUG && console.log("END 'addResultsToGlobal' function"); + } + + /** + * This function is responsible for updating/creating the global violation summary for the engine karma run + * for browser that it is running on. Will take the pageCount object which is part of the page object and + * add extract the values for each of the levels and add them to the global object. This will provide an overall + * summary of violations for all testcases run and all scans done. + * + * @param {Object} pageCount - Provide the page count object, in the following format: + * + * @return N/A - Global summary object is updated with the counts + * + * @memberOf this + */ + var addToSummaryCount = function (pageCount) { + + // Variable Decleration + var ACScanSummary = scanSummary.counts || {}; + var addedToSummary = false; + + // In the case ACScanSummary is empty, simply assign pageCount to ACScanSummary + if (Object.keys(ACScanSummary).length === 0) { + + // Set pageCount as the summary count + ACScanSummary = pageCount; + + addedToSummary = true; + } + + // In the case that this is not first scan, handle adding up the summary + if (!addedToSummary) { + // Go through the pageCount object and for each of the levels, extract the value + // and add it to the aChecker violation summary object. + // This will keep track of an overall summary of the violations for all testscases, that + // were run for a single karma run. + for (var level in pageCount) { + ACScanSummary[level] += pageCount[level]; + } + } + + // Assign the new violation summary back to the global object + scanSummary.counts = ACScanSummary; + } + + scanSummary = initializeSummary(); + + var myThis = this; + if (typeof(after) !== "undefined" && typeof cy === "undefined") { + after(function(done) { + myThis.onRunComplete(); + done && done(); + }); + } else { + // process.on('beforeExit', function() { + // myThis.onRunComplete(); + // }); + } + Config.DEBUG && console.log("END ACReporter Constructor"); +}; + +// Export this function, which will be called when accessibility-checker loads +module.exports = ACReporterHTML; \ No newline at end of file diff --git a/cypress-accessibility-checker/src/lib/reporters/ACReporterJSON.js b/cypress-accessibility-checker/src/lib/reporters/ACReporterJSON.js new file mode 100644 index 000000000..1eb5b9530 --- /dev/null +++ b/cypress-accessibility-checker/src/lib/reporters/ACReporterJSON.js @@ -0,0 +1,431 @@ +/****************************************************************************** + Copyright:: 2020- IBM, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + *****************************************************************************/ + +// Load all the modules that are needed +var pathLib = require('path'); +var fs = require('fs'); +require("../ACConfigLoader"); + +// Global aChecker Summary Holder +var scanSummary = {}; + +/** + * This function is responsible for constructing the aChecker Reporter which will be used to, report + * the scan results, such as writing the page results and the summary to a JSON file. This reporter function + * is registered with Karma server and triggered based on events that are triggered by the karma communication. + * + * @param {Object} baseReporterDecorator - the base karma reporter, which had the base functions which we override + * @param {Object} config - All the Karma configuration, we will extract what we need from this over + * all object, we need the entire object so that we detect any changes in the object + * as other plugins are loaded and the object is updated dynamically. + * @param {Object} logger - logger object which is used to log debug/error/info messages + * @param {Object} emitter - emitter object which allows to listem on any event that is triggered and allow to execute + * custom code, to handle the event that was triggered. + * + * @return - N/A + * + * @memberOf this + */ +var ACReporterJSON = function (aChecker) { + let Config = aChecker.Config; + Config.DEBUG && console.log("START ACReporter Constructor"); + // Override adapters + this.adapters = []; + + // This emitter function is responsible for calling this function when the info event is detected + this.report = function(info) { + Config.DEBUG && console.log("START 'info' emitter function"); + + // Save the results of a single scan to a JSON file based on the label provided + savePageResults(info); + + // Update the overall summary object count object to include the new scan that was performed + addToSummaryCount(info.summary.counts); + + // Save the summary of this scan into global space of this reporter, to be logged + // once the whole scan is done. + addResultsToGlobal(info); + + Config.DEBUG && console.log("END 'info' emitter function"); + }; + + /** + * This function is responsible for performing any action when the entire karma run is done. + * Overrides onRunComplete function from baseReporterDecorator + * + * @override + * + * @memberOf this + */ + this.onRunComplete = function () { + Config.DEBUG && console.log("START 'ACReporterJSON:onRunComplete' function"); + + // Add End time when the whole karma run is done + // End time will be in milliseconds elapsed since 1 January 1970 00:00:00 UTC up until now. + scanSummary.endReport = Date.now(); + + // Save summary object to a JSON file. + saveSummary(scanSummary); + + Config.DEBUG && console.log("END 'ACReporterJSON:onRunComplete' function"); + }; + + /** + * This function is responsible for saving a single scans results to a file as JSON. On a side note + * this function will also extract the label which will be the file names where the results will be + * saved. + * + * @param {Object} config - Karma config object, used to extrat the outputFolder from the ACConfig. + * @param {Object} results - Provide the scan results for a single page that should be saved. + * + * @memberOf this + */ + var savePageResults = function (results) { + Config.DEBUG && console.log("START 'savePageResults' function"); + + // Extract the outputFolder from the ACConfig (this is the user config that they provid) + var resultDir = Config.outputFolder; + + Config.DEBUG && console.log("Results are going to be stored under results directory: \"" + resultDir + "\""); + + // Build the full file name based on the label provide in the results and also the results dir specified in the + // configuration. + var resultsFileName = pathLib.join(resultDir, results.label + '.json'); + + /**************************************************** DEBUG INFORMATION *************************************************************** + // Debug example which has label which has unix "/" in them. + var resultsFileName = pathLib.join(resultDir, "dependencies/tools-rules-html/v2/a11y/test/g471/Table-layoutMultiple.html.json"); + + // Debug example which has a label which has Windows "\" in them. + var resultsFileName = pathLib.join(resultDir, "dependencies\\tools-rules-html\\v2\\a11y\\test\\g471\\Table-layoutMultiple.html.json"); + ***************************************************************************************************************************************/ + + // Write the results object as JSON to a file. + writeObjectToFileAsJSON(resultsFileName, results); + + Config.DEBUG && console.log("END 'savePageResults' function"); + } + + /** + * This function is responsible for converting a javascript object into JSON and then writing that to a + * json file. + * + * @param {String} fileName - Full path of file where the JSON object should be stored + * @param {String} content - The javascript object which should be converted and saved to file as JSON. + * + * @memberOf this + */ + var writeObjectToFileAsJSON = function (fileName, content) { + Config.DEBUG && console.log("START 'writeObjectToFileAsJSON' function"); + + // Extract the parent directory of the file name that is provided + var parentDir = pathLib.dirname(fileName); + + Config.DEBUG && console.log("Parent Directoy: \"" + parentDir + "\""); + + // In the case that the parent directoy does not exist, create the directories + if (!fs.existsSync(parentDir)) { + + // Create the parent directory recerseivly if it does not exist. + fs.mkdirSync(parentDir, { recursive: true}); + } + + Config.DEBUG && console.log("Object will be written to file: \"" + fileName + "\""); + + // Convert the Object into JSON string and write that to the file + // Make sure to use utf-8 encoding to avoid an issues specify to OS. + // In terms of the JSON string that is constructed use 4 spaces to format the JSON object, before + // writing it to the file. + fs.writeFileSync(fileName, JSON.stringify(content, null, ' '), { encoding: 'utf-8' }); + + Config.DEBUG && console.log("END 'writeObjectToFileAsJSON' function"); + } + + /** + * This function is responsible for initializing the summary object which will store all informations about the + * scans that will occurs while karma is still running and running compliance scans. + * + * @return {Object} scanSummary - return the built scan summary object, which will follow the following format: + * { + * "scanID": "ef3aec68-f073-4f9c-b372-421ae00bd55d", + * "counts": { + * "violation": 0, + * "potentialviolation": 0, + * "recommendation": 0, + * "potentialrecommendation": 0, + * "manual": 0 + * }, + * "startReport": "2016-06-06T00:52:41.603Z", + * "endReport": "", + * "toolID": "karma-ibma-v1.0.0", + * "policies": [ + * "CI162_5_2_DCP070116", + * "CI162_5_2_DCP080115" + * ], + * "reportLevels": [ + * "violation", + * "potentialviolation", + * "recommendation", + * "potentialrecommendation", + * "manual" + * ], + * "labels": [ + * "Firefox", + * "master", + * "V12", + * "Linux" + * ], + * "pageScanSummary": {} + * } + * + * @memberOf this + */ + var initializeSummary = function (config) { + // Variable Decleration + var scanSummary = {}; + var reportLevels = Config.reportLevels; + + // Initialize counts + scanSummary.counts = {} + + // In the case that report levels are provided then populate the count object in + // scanSummary.counts object with the levels which were provided in reportLevels + // array. + if (reportLevels) { + + // Iterate over the report levels and populate the pageResultsWithCount counts + // object + reportLevels.forEach(function (levels) { + scanSummary.counts[levels] = 0; + }); + } + // Populate the scanSummary.counts object with all the levels + else { + scanSummary.counts = { + "violation": 0, + "potentialviolation": 0, + "recommendation": 0, + "potentialrecommendation": 0, + "manual": 0, + "pass": 0 + }; + } + + // Add ignored count disableIgnore field in config file is not true + if(Config.disableIgnore == undefined || Config.disableIgnore == false || Config.disableIgnore == null){ + scanSummary.counts.ignored = 0; + } + + // Add Start time when this script is loaded into browser + // Start time will be in milliseconds elapsed since 1 January 1970 00:00:00 UTC up until now. + scanSummary.startReport = Date.now(); + + // Leave end report as empty for now + scanSummary.endReport = ''; + + // Add the toolID, ruleArchive, policies, reportLevels, failLevels and labels from the config to the summary + scanSummary.toolID = Config.toolID; + // Append the id and the name of the archive to the report. + scanSummary.policies = Config.policies; + scanSummary.ruleArchive = Config.ruleArchive; + scanSummary.reportLevels = Config.reportLevels; + scanSummary.labels = Config.label; + scanSummary.failLevels = Config.failLevels; + + // Add scanID (UUID) to the scan summary object + scanSummary.scanID = Config.scanID; + + // Build the paceScanSummary object which will contains all the items that were scanned and a count + // summary for each of the scanned items. + scanSummary.pageScanSummary = []; + + return scanSummary; + } + + /** + * This function is responsible for saving the summary object of the while scan to a summary file. + * + * @param {Object} summary - The summary object that needs to be written to the summary file. + * + * @memberOf this + */ + var saveSummary = function (summary) { + if (Config.outputFormat.indexOf("json") === -1) { + return; + } + Config.DEBUG && console.log("START 'saveSummary' function"); + + // Fetch the start time of the report from the summary object + var startReportTime = summary.startReport; + + // Extract the outputFolder from the ACConfig (this is the user config that they provid) + var resultDir = Config.outputFolder; + + Config.DEBUG && console.log("Converting: " + startReportTime); + + // Now we need to take the from epoch format date and convert it to readable data + // Construct a new Data object with the start report time + var formattedData = new Date(startReportTime); + + // Extract all the date fields which are needed to construct the filename + var year = datePadding(formattedData.getUTCFullYear()); + var month = datePadding(formattedData.getUTCMonth()+1); // UTC Month is provid in a range of A Number, from 0-11, representing the month + var date = datePadding(formattedData.getUTCDate()); + var hour = datePadding(formattedData.getHours()); + var minute = datePadding(formattedData.getMinutes()); + var seconds = datePadding(formattedData.getUTCSeconds()); + + Config.DEBUG && console.log("Year: " + year); + Config.DEBUG && console.log("Month: " + month); + Config.DEBUG && console.log("Date: " + date); + Config.DEBUG && console.log("Hour: " + hour); + Config.DEBUG && console.log("Minute: " + minute); + Config.DEBUG && console.log("Seconds: " + seconds); + + // Build the summary file name based on the following format: summary_2016-06-20-13-26-45GMT.json + // summary_<year>-<month>-<date>-<hour>-<minute>-<seconds>GMT.json + var filename = "summary_" + year + "-" + month + "-" + date + "-" + hour + "-" + minute + "-" + seconds + "GMT.json"; + + // Add the results dir to the filename so that all the summary files can be saved under the output folder + filename = pathLib.join(resultDir, filename); + + Config.DEBUG && console.log("Filename Constructed: " + filename); + + // Write the summary object as json to the summary file. + writeObjectToFileAsJSON(filename, summary); + + Config.DEBUG && console.log("END 'saveSummary' function"); + } + + /** + * This function is responsible for checking if a number needs a leading 0, and if it is needed + * add the leading 0 and return that as the new number. + * + * @param {int} number - Provide a number to check if a leading 0 needs to be added or not. + * + * @return {String} number - Return the number with the leading 0 added back + * + * @memberOf this + */ + var datePadding = function(number) { + Config.DEBUG && console.log("START 'datePadding' function"); + + // In the case that the number is less then 10 we need to add the leading '0' to the number. + number = number < 10 ? '0' + number : number; + + Config.DEBUG && console.log("END 'datePadding' function"); + + return number; + } + + /** + * This function is responsible for indexing the results into global spaces based on label. + * + * @param {Object} results - Results object which will be provided to the user/wroten to the file. + * Refer to aChecker.buildReport function's return to figure out what the object + * will look like. + * + * @return - N/A - Global object is updated with the results + * + * @memberOf this + */ + var addResultsToGlobal = function (results) { + Config.DEBUG && console.log("START 'addResultsToGlobal' function"); + + // Build the single page summary object to follow the following format: + // "label": "dependencies/tools-rules-html/v2/a11y/test/g471/Table-DataNoSummaryARIA.html", + // "counts": { + // "violation": 1, + // "potentialviolation": 0, + // "recommendation": 0, + // "potentialrecommendation": 0, + // "manual": 0 + // } + var pageSummaryObject = { + label: results.label, + counts: results.summary.counts + } + + Config.DEBUG && console.log("Adding following object to scanSummary.pageScanSummary: "); + Config.DEBUG && console.log(pageSummaryObject); + + // Add the summary count for this scan to the pageScanSummary object which is in the global space + // Index this by the label. + scanSummary.pageScanSummary.push(pageSummaryObject); + + Config.DEBUG && console.log("END 'addResultsToGlobal' function"); + } + + /** + * This function is responsible for updating/creating the global violation summary for the engine karma run + * for browser that it is running on. Will take the pageCount object which is part of the page object and + * add extract the values for each of the levels and add them to the global object. This will provide an overall + * summary of violations for all testcases run and all scans done. + * + * @param {Object} pageCount - Provide the page count object, in the following format: + * + * @return N/A - Global summary object is updated with the counts + * + * @memberOf this + */ + var addToSummaryCount = function (pageCount) { + + // Variable Decleration + var ACScanSummary = scanSummary.counts || {}; + var addedToSummary = false; + + // In the case ACScanSummary is empty, simply assign pageCount to ACScanSummary + if (Object.keys(ACScanSummary).length === 0) { + + // Set pageCount as the summary count + ACScanSummary = pageCount; + + addedToSummary = true; + } + + // In the case that this is not first scan, handle adding up the summary + if (!addedToSummary) { + // Go through the pageCount object and for each of the levels, extract the value + // and add it to the aChecker violation summary object. + // This will keep track of an overall summary of the violations for all testscases, that + // were run for a single karma run. + for (var level in pageCount) { + ACScanSummary[level] += pageCount[level]; + } + } + + // Assign the new violation summary back to the global object + scanSummary.counts = ACScanSummary; + } + + scanSummary = initializeSummary(); + + var myThis = this; + if (typeof(after) !== "undefined" && typeof cy === "undefined") { + after(function(done) { + myThis.onRunComplete(); + done && done(); + }); + } else { + // process.on('beforeExit', function() { + // myThis.onRunComplete(); + // }); + } + Config.DEBUG && console.log("END ACReporter Constructor"); +}; + +// Export this function, which will be called when accessibility-checker loads +module.exports = ACReporterJSON; \ No newline at end of file diff --git a/cypress-accessibility-checker/src/tasks.js b/cypress-accessibility-checker/src/tasks.js index b1ba4f651..001f7f444 100644 --- a/cypress-accessibility-checker/src/tasks.js +++ b/cypress-accessibility-checker/src/tasks.js @@ -14,22 +14,30 @@ limitations under the License. *****************************************************************************/ -const aChecker = require('accessibility-checker'); +const ACTasks = require("./lib/ACTasks"); -/** - * Object format: - * - * html: HTML string to scan - * label: Label of report - */ -function getCompliance({ html, label }) { - return new Promise((resolve, reject) => { - aChecker.getCompliance(html, label).then( - /* Only send back the report. If we send back Puppeteer object is creates circular JSON and breaks. */ - (result) => resolve({ report: result.report }), - () => reject('A11y: Failed to get compliance') - ); - }); +// /** +// * Object format: +// * +// * html: HTML string to scan +// * label: Label of report +// */ +// function getCompliance({ html, label }) { +// return new Promise((resolve, reject) => { +// ACTasks.getCompliance(html, label).then( +// /* Only send back the report. If we send back Puppeteer object is creates circular JSON and breaks. */ +// (result) => resolve({ report: result.report }), +// () => reject('accessibility-checker: Failed to get compliance') +// ); +// }); +// } + +function loadBaselines() { + return ACTasks.loadBaselines(); +} + +function onRunComplete() { + return ACTasks.onRunComplete(); } /** @@ -38,7 +46,7 @@ function getCompliance({ html, label }) { * report: Report data from `getCompliance()` call */ function assertCompliance({ report }) { - return aChecker.assertCompliance(report); + return ACTasks.assertCompliance(report); } /** @@ -47,7 +55,7 @@ function assertCompliance({ report }) { * label: Label of the report to diff against. */ function getDiffResults({ label }) { - return aChecker.getDiffResults(label); + return ACTasks.getDiffResults(label); } /** @@ -56,7 +64,7 @@ function getDiffResults({ label }) { * label: Label of the baseline results to return. */ function getBaseline({ label }) { - return aChecker.getBaseline(label); + return ACTasks.getBaseline(label); } /** @@ -67,7 +75,7 @@ function getBaseline({ label }) { * clean: Whether or not to clean the results by converting the objects to match basic compliance of only xpath and ruleid. */ function diffResultsWithExpected({ actual, expected, clean }) { - return aChecker.diffResultsWithExpected(actual, expected, clean); + return ACTasks.diffResultsWithExpected(actual, expected, clean); } /** @@ -76,7 +84,7 @@ function diffResultsWithExpected({ actual, expected, clean }) { * report: Report to be stringified. */ function stringifyResults({ report }) { - return aChecker.stringifyResults(report); + return ACTasks.stringifyResults(report); } /** @@ -85,26 +93,12 @@ function stringifyResults({ report }) { * N/A */ function getConfig() { - return new Promise((resolve, reject) => { - aChecker.getConfig().then( - (config) => resolve(config), - () => reject('A11y: Failed to get config') - ); - }); -} - -/** - * Object format: - * - * N/A - */ -function close() { - return new Promise((resolve, reject) => { - aChecker.close().then( - () => resolve(true), - () => reject('A11y: Failed to close resources') - ); - }); + return new Promise((resolve, reject) => { + ACTasks.getConfig().then( + (config) => resolve(config), + () => reject('accessibility-checker: Failed to get config') + ); + }); } /** @@ -114,26 +108,26 @@ function close() { * data: Object - Data to pass to the task. */ module.exports = ({ task, data }) => { - switch (task) { - case 'getCompliance': - return getCompliance(data); - case 'assertCompliance': - return assertCompliance(data); - case 'getDiffResults': - return getDiffResults(data); - case 'getBaseline': - return getBaseline(data); - case 'diffResultsWithExpected': - return diffResultsWithExpected(data); - case 'stringifyResults': - return stringifyResults(data); - case 'getConfig': - return getConfig(data); - case 'close': - return close(data); - default: - throw new Error( - 'accessibility-checker: Invalid task ID sent. Accessibility checker tasks should only be called by the accessibility-checker commands.' - ); - } + switch (task) { + case 'sendResultsToReporter': + return ACTasks.sendResultsToReporter(data.origReport, data.report); + case 'assertCompliance': + return assertCompliance(data); + case 'getBaseline': + return getBaseline(data); + case 'diffResultsWithExpected': + return diffResultsWithExpected(data); + case 'stringifyResults': + return stringifyResults(data); + case 'getConfig': + return getConfig(data); + case 'loadBaselines': + return loadBaselines(); + case 'onRunComplete': + return onRunComplete(); + default: + throw new Error( + 'accessibility-checker: Invalid task ID sent. Accessibility checker tasks should only be called by the accessibility-checker commands.' + ); + } }; diff --git a/cypress-accessibility-checker/test/.achecker.yml b/cypress-accessibility-checker/test/.achecker.yml index ec636b1ca..0e2fab111 100644 --- a/cypress-accessibility-checker/test/.achecker.yml +++ b/cypress-accessibility-checker/test/.achecker.yml @@ -38,6 +38,8 @@ reportLevels: # Default: json outputFormat: - json + - html + - csv # Optional - Specify labels that you would like associated to your scan # diff --git a/cypress-accessibility-checker/test/baselines/getBaseline test.json b/cypress-accessibility-checker/test/baselines/getBaseline test.json new file mode 100644 index 000000000..cdc7f1b8a --- /dev/null +++ b/cypress-accessibility-checker/test/baselines/getBaseline test.json @@ -0,0 +1,209 @@ +{ + "results": [ + { + "ruleId": "WCAG20_Html_HasLang", + "value": [ + "VIOLATION", + "FAIL" + ], + "path": { + "dom": "/html[1]", + "aria": "" + }, + "ruleTime": 0, + "reasonId": "Fail_3", + "message": "Page detected as HTML, but does not have a 'lang' attribute", + "messageArgs": [], + "apiArgs": [], + "bounds": { + "left": 0, + "top": 0, + "height": 660, + "width": 1000 + }, + "snippet": "<html>", + "category": "Accessibility", + "ignored": false, + "level": "violation" + }, + { + "ruleId": "WCAG20_Body_FirstASkips_Native_Host_Sematics", + "value": [ + "VIOLATION", + "FAIL" + ], + "path": { + "dom": "/html[1]/body[1]", + "aria": "/document[1]" + }, + "ruleTime": 0, + "reasonId": "Fail_1", + "message": "The page does not provide a way to quickly navigate to the main content (WAI-ARIA \"main\" landmark or a skip link)", + "messageArgs": [], + "apiArgs": [], + "bounds": { + "left": 8, + "top": 8, + "height": 644, + "width": 984 + }, + "snippet": "<body>", + "category": "Accessibility", + "ignored": false, + "level": "violation" + }, + { + "ruleId": "Rpt_Aria_OrphanedContent_Native_Host_Sematics", + "value": [ + "VIOLATION", + "FAIL" + ], + "path": { + "dom": "/html[1]/body[1]/h1[1]", + "aria": "/document[1]/heading[1]" + }, + "ruleTime": 0, + "reasonId": "Fail_1", + "message": "Content is not within a landmark element", + "messageArgs": [], + "apiArgs": [], + "bounds": { + "left": 8, + "top": 8, + "height": 37, + "width": 984 + }, + "snippet": "<h1>", + "category": "Accessibility", + "ignored": false, + "level": "violation" + }, + { + "ruleId": "WCAG20_Img_HasAlt", + "value": [ + "VIOLATION", + "FAIL" + ], + "path": { + "dom": "/html[1]/body[1]/img[1]", + "aria": "/document[1]/img[1]" + }, + "ruleTime": 0, + "reasonId": "Fail_2", + "message": "Image does not have an 'alt' attribute short text alternative", + "messageArgs": [], + "apiArgs": [], + "bounds": { + "left": 8, + "top": 67, + "height": 16, + "width": 16 + }, + "snippet": "<img src=\"missing-alt.jpg\">", + "category": "Accessibility", + "ignored": false, + "level": "violation" + } + ], + "numExecuted": 68, + "nls": { + "WCAG20_Html_HasLang": { + "0": "Page must identify the default language of the document with a 'lang' attribute", + "Fail_3": "Page detected as HTML, but does not have a 'lang' attribute" + }, + "RPT_Html_SkipNav": { + "0": "Provide a way to bypass blocks of content that are repeated on multiple Web pages", + "Pass_0": "Rule Passed" + }, + "WCAG20_Doc_HasTitle": { + "0": "The page should have a title that correctly identifies the subject of the page", + "Pass_0": "Rule Passed" + }, + "HAAC_BackgroundImg_HasTextOrTitle": { + "0": "Background images that convey important information must have a text alternative that describes the image", + "Pass_0": "Rule Passed" + }, + "RPT_List_UseMarkup": { + "0": "Use proper HTML list elements to create lists", + "Pass_0": "Rule Passed" + }, + "RPT_Text_SensoryReference": { + "0": "Instructions must be meaningful without shape or location words", + "Pass_0": "Rule Passed" + }, + "WCAG20_Text_Emoticons": { + "0": "Emoticons must have a short text alternative that describes their purpose", + "Pass_0": "Rule Passed" + }, + "WCAG20_Text_LetterSpacing": { + "0": "Use CSS 'letter-spacing' to control spacing within a word", + "Pass_0": "Rule Passed" + }, + "RPT_Blockquote_WrapsTextQuote": { + "0": "Quotations should be marked with <q> or <blockquote> elements", + "Pass_0": "Rule Passed" + }, + "IBMA_Color_Contrast_WCAG2AA_PV": { + "0": "The contrast ratio of text with its background (i.e. background with a color gradient or a background image) must meet WCAG 2.1 AA requirements", + "Pass_0": "Rule Passed" + }, + "HAAC_Aria_Native_Host_Sematics": { + "0": "WAI-ARIA roles and attributes must be valid for the element they are assigned to", + "Pass_0": "Rule Passed" + }, + "Rpt_Aria_OrphanedContent_Native_Host_Sematics": { + "0": "All content must reside within an element with a landmark role", + "Pass_0": "Rule Passed", + "Fail_1": "Content is not within a landmark element" + }, + "RPT_Title_Valid": { + "0": "Page <title> should be a descriptive title, rather than a filename", + "Pass_0": "Rule Passed" + }, + "WCAG20_Body_FirstASkips_Native_Host_Sematics": { + "0": "Pages must provide a way to skip directly to the main content", + "Fail_1": "The page does not provide a way to quickly navigate to the main content (WAI-ARIA \"main\" landmark or a skip link)" + }, + "RPT_Header_HasContent": { + "0": "Heading elements must provide descriptive text", + "Pass_0": "Rule Passed" + }, + "IBMA_Color_Contrast_WCAG2AA": { + "0": "The contrast ratio of text with its background must meet WCAG 2.1 AA requirements", + "Pass_0": "Rule Passed" + }, + "RPT_Headers_FewWords": { + "0": "Heading elements must not be used for presentation", + "Pass_0": "Rule Passed" + }, + "WCAG20_Img_HasAlt": { + "0": "Images must have an 'alt' attribute with a short text alternative if they convey meaning, or 'alt=\"\" if decorative", + "Fail_2": "Image does not have an 'alt' attribute short text alternative" + }, + "RPT_Img_AltCommonMisuse": { + "0": "'alt' attribute value must be a good inline replacement for the image", + "Pass_0": "Rule Passed" + } + }, + "summary": { + "counts": { + "violation": 4, + "potentialviolation": 0, + "ignored": 0 + }, + "scanTime": 8, + "ruleArchive": "Latest Deployment (latest)", + "policies": [ + "IBM_Accessibility" + ], + "reportLevels": [ + "violation", + "potentialviolation" + ], + "startScan": 1601305960181, + "URL": "http://localhost:8080/test/sample-html/violations.html" + }, + "scanID": "c2f76bde-b685-4c2c-b266-d5567e47f4b7", + "toolID": "cypress-accessibility-checker-v3.0.0", + "label": "getBaseline test" +} \ No newline at end of file diff --git a/cypress-accessibility-checker/test/baselines/no-violations.json b/cypress-accessibility-checker/test/baselines/no-violations.json index a17272782..e958d92fb 100644 --- a/cypress-accessibility-checker/test/baselines/no-violations.json +++ b/cypress-accessibility-checker/test/baselines/no-violations.json @@ -1,143 +1,139 @@ { - "results": [], - "numExecuted": 85, - "nls": { - "WCAG20_Html_HasLang": { - "0": "Page must identify the default language of the document with a lang attribute", - "Pass_0": "Page language detected as {0}" - }, - "RPT_Html_SkipNav": { - "0": "Provide a mechanism to bypass blocks of content that are repeated on multiple Web pages.", - "Pass_0": "Rule Passed" - }, - "WCAG20_Doc_HasTitle": { - "0": "The page should have a title that correctly identifies the subject of the page.", - "Pass_0": "Rule Passed" - }, - "WCAG20_Elem_Lang_Valid": { - "0": "Lang and/or xml:lang attribute on any element on the page to identify change in language must conform to BCP: 47.", - "Pass_0": "Rule Passed" - }, - "HAAC_BackgroundImg_HasTextOrTitle": { - "0": "Verify that important background image information is conveyed when the system is in high contrast mode.", - "Pass_0": "Rule Passed" - }, - "RPT_List_UseMarkup": { - "0": "Use proper HTML LIST elements to create lists.", - "Pass_0": "Rule Passed" - }, - "RPT_Text_SensoryReference": { - "0": "Usage of sensory words must be meaningful to users who may not have sensory perception of size, sound, shape, or location.", - "Pass_0": "Rule Passed" - }, - "WCAG20_Text_Emoticons": { - "0": "Provide text alternatives for emoticons.", - "Pass_0": "Rule Passed" - }, - "WCAG20_Text_LetterSpacing": { - "0": "Use CSS 'letter-spacing' to control spacing within a word.", - "Pass_0": "Rule Passed" - }, - "RPT_Blockquote_WrapsTextQuote": { - "0": "Quotations should be marked with Q or BLOCKQUOTE elements.", - "Pass_0": "Rule Passed" - }, - "IBMA_Color_Contrast_WCAG2AA_PV": { - "0": "Contrast of foreground and background must be sufficient to meet WCAG AA requirements.", - "Pass_0": "Rule Passed" - }, - "HAAC_Aria_Native_Host_Sematics": { - "0": "The WAI-ARIA role(s) and/or attribute(s) must be valid for the relevant element.", - "Pass_0": "Rule Passed" - }, - "Rpt_Aria_OrphanedContent_Native_Host_Sematics": { - "0": "All content must reside within a HTML5 Sectioning element, WAI-ARIA landmark or labelled region role.", - "Pass_0": "Rule Passed" - }, - "RPT_Title_Valid": { - "0": "Provide a descriptive TITLE for Web pages.", - "Pass_0": "Rule Passed" - }, - "WCAG20_Body_FirstASkips_Native_Host_Sematics": { - "0": "The page must provide a link to skip directly to the main page content as the first link on each page or use WAI-ARIA landmarks.", - "Pass_0": "Rule Passed" - }, - "WCAG20_Body_FirstAContainsSkipText_Native_Host_Sematics": { - "0": "Check that the description of the first hyperlink communicates that it links to the main content.", - "Pass_0": "Rule Passed" - }, - "RPT_Block_ShouldBeHeading": { - "0": "Use HEADER element to define text that looks like a heading where appropriate.", - "Pass_0": "Rule Passed" - }, - "HAAC_Application_Role_Text": { - "0": "Non-decorative static text or images within \"application\" role must receive accessible focus.", - "Pass_0": "Rule Passed" - }, - "Rpt_Aria_ValidRole": { - "0": "Elements that use 'role' must reference a valid WAI-ARIA role.", - "Pass_0": "Rule Passed" - }, - "Rpt_Aria_RequiredChildren_Native_Host_Sematics": { - "0": "An element with WAI-ARIA 'role' must contain required children.", - "Pass_0": "Rule Passed" - }, - "Rpt_Aria_MissingKeyboardHandler": { - "0": "Keyboard handlers must be provided", - "Pass_0": "Rule Passed" - }, - "RPT_Header_HasContent": { - "0": "Provide descriptive headings for Web page sections.", - "Pass_0": "Rule Passed" - }, - "IBMA_Color_Contrast_WCAG2AA": { - "0": "Contrast of foreground and background must be sufficient to meet WCAG AA requirements.", - "Pass_0": "Rule Passed" - }, - "RPT_Headers_FewWords": { - "0": "Make sure HEADER elements are used as a heading for a section and not used to format text for presentation.", - "Pass_0": "Rule Passed" - }, - "WCAG20_Img_HasAlt": { - "0": "Images must have an alt attribute, and must be the empty string if decorative.", - "Pass_0": "Rule Passed" - }, - "WCAG20_Img_PresentationImgHasNonNullAlt": { - "0": "An Image with presentation role or with a role of none must have a null alt attribute.", - "Pass_0": "Rule Passed" - }, - "RPT_Img_AltCommonMisuse": { - "0": "Ensure that image alt text serves as an inline replacement of the image.", - "Pass_0": "Rule Passed" - }, - "RPT_Media_AltBrief": { - "0": "Alt text should be brief, if >150 characters, consider providing a separate additional description.", - "Pass_0": "Rule Passed" - } - }, - "summary": { - "counts": { - "violation": 0, - "potentialviolation": 0, - "recommendation": 0, - "potentialrecommendation": 0, - "manual": 0, - "ignored": 0 - }, - "scanTime": 169, - "ruleArchive": "Latest Archive (latest)", - "policies": ["IBM_Accessibility"], - "reportLevels": [ - "violation", - "potentialviolation", - "recommendation", - "potentialrecommendation", - "manual" - ], - "startScan": 1588966910823, - "URL": "about:blank" - }, - "scanID": "30f701d0-5283-46ed-a653-7a3787a8d27d", - "toolID": "accessibility-checker-v3.0.0-beta.2", - "label": "assert compliance rc 0 no baseline" -} + "results": [], + "numExecuted": 85, + "nls": { + "WCAG20_Html_HasLang": { + "0": "Page must identify the default language of the document with a 'lang' attribute", + "Pass_0": "Page language detected as \"{0}\"" + }, + "RPT_Html_SkipNav": { + "0": "Provide a way to bypass blocks of content that are repeated on multiple Web pages", + "Pass_0": "Rule Passed" + }, + "WCAG20_Doc_HasTitle": { + "0": "The page should have a title that correctly identifies the subject of the page", + "Pass_0": "Rule Passed" + }, + "WCAG20_Elem_Lang_Valid": { + "0": "The language of content must be specified in accordance with BCP 47", + "Pass_0": "Rule Passed" + }, + "HAAC_BackgroundImg_HasTextOrTitle": { + "0": "Background images that convey important information must have a text alternative that describes the image", + "Pass_0": "Rule Passed" + }, + "RPT_List_UseMarkup": { + "0": "Use proper HTML list elements to create lists", + "Pass_0": "Rule Passed" + }, + "RPT_Text_SensoryReference": { + "0": "Instructions must be meaningful without shape or location words", + "Pass_0": "Rule Passed" + }, + "WCAG20_Text_Emoticons": { + "0": "Emoticons must have a short text alternative that describes their purpose", + "Pass_0": "Rule Passed" + }, + "WCAG20_Text_LetterSpacing": { + "0": "Use CSS 'letter-spacing' to control spacing within a word", + "Pass_0": "Rule Passed" + }, + "RPT_Blockquote_WrapsTextQuote": { + "0": "Quotations should be marked with <q> or <blockquote> elements", + "Pass_0": "Rule Passed" + }, + "IBMA_Color_Contrast_WCAG2AA_PV": { + "0": "The contrast ratio of text with its background (i.e. background with a color gradient or a background image) must meet WCAG 2.1 AA requirements", + "Pass_0": "Rule Passed" + }, + "HAAC_Aria_Native_Host_Sematics": { + "0": "WAI-ARIA roles and attributes must be valid for the element they are assigned to", + "Pass_0": "Rule Passed" + }, + "Rpt_Aria_OrphanedContent_Native_Host_Sematics": { + "0": "All content must reside within an element with a landmark role", + "Pass_0": "Rule Passed" + }, + "RPT_Title_Valid": { + "0": "Page <title> should be a descriptive title, rather than a filename", + "Pass_0": "Rule Passed" + }, + "WCAG20_Body_FirstASkips_Native_Host_Sematics": { + "0": "Pages must provide a way to skip directly to the main content", + "Pass_0": "Rule Passed" + }, + "WCAG20_Body_FirstAContainsSkipText_Native_Host_Sematics": { + "0": "The description of a hyperlink used to skip content must communicate where it links to", + "Pass_0": "Rule Passed" + }, + "RPT_Block_ShouldBeHeading": { + "0": "Heading text must use a heading element", + "Pass_0": "Rule Passed" + }, + "HAAC_Application_Role_Text": { + "0": "Non-decorative static text and image content within an element with \"application\" role must be accessible", + "Pass_0": "Rule Passed" + }, + "Rpt_Aria_ValidRole": { + "0": "Elements must have a valid 'role' per WAI-ARIA specification", + "Pass_0": "Rule Passed" + }, + "Rpt_Aria_RequiredChildren_Native_Host_Sematics": { + "0": "An element with a WAI-ARIA role must contain required children", + "Pass_0": "Rule Passed" + }, + "Rpt_Aria_MissingKeyboardHandler": { + "0": "Interactive WAI_ARIA UI components must provide keyboard access", + "Pass_0": "Rule Passed" + }, + "RPT_Header_HasContent": { + "0": "Heading elements must provide descriptive text", + "Pass_0": "Rule Passed" + }, + "IBMA_Color_Contrast_WCAG2AA": { + "0": "The contrast ratio of text with its background must meet WCAG 2.1 AA requirements", + "Pass_0": "Rule Passed" + }, + "RPT_Headers_FewWords": { + "0": "Heading elements must not be used for presentation", + "Pass_0": "Rule Passed" + }, + "WCAG20_Img_HasAlt": { + "0": "Images must have an 'alt' attribute with a short text alternative if they convey meaning, or 'alt=\"\" if decorative", + "Pass_0": "Rule Passed" + }, + "WCAG20_Img_PresentationImgHasNonNullAlt": { + "0": "Image designated as decorative must have 'alt=\"\"", + "Pass_0": "Rule Passed" + }, + "RPT_Img_AltCommonMisuse": { + "0": "'alt' attribute value must be a good inline replacement for the image", + "Pass_0": "Rule Passed" + }, + "RPT_Media_AltBrief": { + "0": "Alternative text in 'alt' attribute should be brief (<150 characters)", + "Pass_0": "Rule Passed" + } + }, + "summary": { + "counts": { + "violation": 0, + "potentialviolation": 0, + "ignored": 0 + }, + "scanTime": 10, + "ruleArchive": "Latest Deployment (latest)", + "policies": [ + "IBM_Accessibility" + ], + "reportLevels": [ + "violation", + "potentialviolation" + ], + "startScan": 1601305147365, + "URL": "http://localhost:8080/test/sample-html/no-violations.html" + }, + "scanID": "25bdec16-c677-4773-afdb-56319dffc0b7", + "toolID": "cypress-accessibility-checker-v3.0.0", + "label": "no-violations" +} \ No newline at end of file diff --git a/cypress-accessibility-checker/test/baselines/violations-no-match.json b/cypress-accessibility-checker/test/baselines/violations-no-match.json new file mode 100644 index 000000000..a3c02d0bf --- /dev/null +++ b/cypress-accessibility-checker/test/baselines/violations-no-match.json @@ -0,0 +1,235 @@ +{ + "results": [ + { + "ruleId": "WCAG20_Html_HasLang", + "value": [ + "VIOLATION", + "FAIL" + ], + "path": { + "dom": "/html[1]", + "aria": "" + }, + "ruleTime": 0, + "reasonId": "Fail_3", + "message": "Page detected as HTML, but does not have a 'lang' attribute", + "messageArgs": [], + "apiArgs": [], + "bounds": { + "left": 0, + "top": 0, + "height": 1320, + "width": 2000 + }, + "snippet": "<html>", + "category": "Accessibility", + "ignored": false, + "level": "violation" + }, + { + "ruleId": "WCAG20_Html_HasLang", + "value": [ + "VIOLATION", + "FAIL" + ], + "path": { + "dom": "/html[1]/body[1]", + "aria": "" + }, + "ruleTime": 0, + "reasonId": "Fail_3", + "message": "Page detected as HTML, but does not have a 'lang' attribute", + "messageArgs": [], + "apiArgs": [], + "bounds": { + "left": 0, + "top": 0, + "height": 1320, + "width": 2000 + }, + "snippet": "<html>", + "category": "Accessibility", + "ignored": false, + "level": "violation" + }, + { + "ruleId": "WCAG20_Body_FirstASkips_Native_Host_Sematics", + "value": [ + "VIOLATION", + "FAIL" + ], + "path": { + "dom": "/html[1]/body[1]", + "aria": "/document[1]" + }, + "ruleTime": 0, + "reasonId": "Fail_1", + "message": "The page does not provide a way to quickly navigate to the main content (WAI-ARIA \"main\" landmark or a skip link)", + "messageArgs": [], + "apiArgs": [], + "bounds": { + "left": 16, + "top": 16, + "height": 1288, + "width": 1968 + }, + "snippet": "<body>", + "category": "Accessibility", + "ignored": false, + "level": "violation" + }, + { + "ruleId": "Rpt_Aria_OrphanedContent_Native_Host_Sematics", + "value": [ + "VIOLATION", + "FAIL" + ], + "path": { + "dom": "/html[1]/body[1]/h1[1]", + "aria": "/document[1]/heading[1]" + }, + "ruleTime": 0, + "reasonId": "Fail_1", + "message": "Content is not within a landmark element", + "messageArgs": [], + "apiArgs": [], + "bounds": { + "left": 16, + "top": 16, + "height": 74, + "width": 1968 + }, + "snippet": "<h1>", + "category": "Accessibility", + "ignored": false, + "level": "violation" + }, + { + "ruleId": "WCAG20_Img_HasAlt", + "value": [ + "VIOLATION", + "FAIL" + ], + "path": { + "dom": "/html[1]/body[1]/img[1]", + "aria": "/document[1]/img[1]" + }, + "ruleTime": 0, + "reasonId": "Fail_2", + "message": "Image does not have an 'alt' attribute short text alternative", + "messageArgs": [], + "apiArgs": [], + "bounds": { + "left": 16, + "top": 133, + "height": 32, + "width": 32 + }, + "snippet": "<img src=\"missing-alt.jpg\">", + "category": "Accessibility", + "ignored": false, + "level": "violation" + } + ], + "numExecuted": 68, + "nls": { + "WCAG20_Html_HasLang": { + "0": "Page must identify the default language of the document with a 'lang' attribute", + "Fail_3": "Page detected as HTML, but does not have a 'lang' attribute" + }, + "RPT_Html_SkipNav": { + "0": "Provide a way to bypass blocks of content that are repeated on multiple Web pages", + "Pass_0": "Rule Passed" + }, + "WCAG20_Doc_HasTitle": { + "0": "The page should have a title that correctly identifies the subject of the page", + "Pass_0": "Rule Passed" + }, + "HAAC_BackgroundImg_HasTextOrTitle": { + "0": "Background images that convey important information must have a text alternative that describes the image", + "Pass_0": "Rule Passed" + }, + "RPT_List_UseMarkup": { + "0": "Use proper HTML list elements to create lists", + "Pass_0": "Rule Passed" + }, + "RPT_Text_SensoryReference": { + "0": "Instructions must be meaningful without shape or location words", + "Pass_0": "Rule Passed" + }, + "WCAG20_Text_Emoticons": { + "0": "Emoticons must have a short text alternative that describes their purpose", + "Pass_0": "Rule Passed" + }, + "WCAG20_Text_LetterSpacing": { + "0": "Use CSS 'letter-spacing' to control spacing within a word", + "Pass_0": "Rule Passed" + }, + "RPT_Blockquote_WrapsTextQuote": { + "0": "Quotations should be marked with <q> or <blockquote> elements", + "Pass_0": "Rule Passed" + }, + "IBMA_Color_Contrast_WCAG2AA_PV": { + "0": "The contrast ratio of text with its background (i.e. background with a color gradient or a background image) must meet WCAG 2.1 AA requirements", + "Pass_0": "Rule Passed" + }, + "HAAC_Aria_Native_Host_Sematics": { + "0": "WAI-ARIA roles and attributes must be valid for the element they are assigned to", + "Pass_0": "Rule Passed" + }, + "Rpt_Aria_OrphanedContent_Native_Host_Sematics": { + "0": "All content must reside within an element with a landmark role", + "Pass_0": "Rule Passed", + "Fail_1": "Content is not within a landmark element" + }, + "RPT_Title_Valid": { + "0": "Page <title> should be a descriptive title, rather than a filename", + "Pass_0": "Rule Passed" + }, + "WCAG20_Body_FirstASkips_Native_Host_Sematics": { + "0": "Pages must provide a way to skip directly to the main content", + "Fail_1": "The page does not provide a way to quickly navigate to the main content (WAI-ARIA \"main\" landmark or a skip link)" + }, + "RPT_Header_HasContent": { + "0": "Heading elements must provide descriptive text", + "Pass_0": "Rule Passed" + }, + "IBMA_Color_Contrast_WCAG2AA": { + "0": "The contrast ratio of text with its background must meet WCAG 2.1 AA requirements", + "Pass_0": "Rule Passed" + }, + "RPT_Headers_FewWords": { + "0": "Heading elements must not be used for presentation", + "Pass_0": "Rule Passed" + }, + "WCAG20_Img_HasAlt": { + "0": "Images must have an 'alt' attribute with a short text alternative if they convey meaning, or 'alt=\"\" if decorative", + "Fail_2": "Image does not have an 'alt' attribute short text alternative" + }, + "RPT_Img_AltCommonMisuse": { + "0": "'alt' attribute value must be a good inline replacement for the image", + "Pass_0": "Rule Passed" + } + }, + "summary": { + "counts": { + "violation": 4, + "potentialviolation": 0, + "ignored": 0 + }, + "scanTime": 8, + "ruleArchive": "Latest Deployment (latest)", + "policies": [ + "IBM_Accessibility" + ], + "reportLevels": [ + "violation", + "potentialviolation" + ], + "startScan": 1601312544640, + "URL": "http://localhost:8080/test/sample-html/violations.html" + }, + "scanID": "5773ad6e-e758-4656-8784-af63c59944fe", + "toolID": "cypress-accessibility-checker-v3.0.0", + "label": "violations-no-match" +} \ No newline at end of file diff --git a/cypress-accessibility-checker/test/baselines/violations.json b/cypress-accessibility-checker/test/baselines/violations.json index 13a06b9ed..c7c233410 100644 --- a/cypress-accessibility-checker/test/baselines/violations.json +++ b/cypress-accessibility-checker/test/baselines/violations.json @@ -1,201 +1,209 @@ { - "results": [ - { - "ruleId": "WCAG20_Html_HasLang", - "value": ["VIOLATION", "FAIL"], - "path": { - "dom": "/html[1]", - "aria": "" - }, - "ruleTime": 0, - "reasonId": "Fail_3", - "message": "Page detected as HTML, but does not have a lang attribute.", - "messageArgs": [], - "apiArgs": [], - "bounds": { - "left": 0, - "top": 0, - "height": 0, - "width": 0 - }, - "snippet": "<html>", - "category": "Accessibility", - "ignored": false, - "level": "violation" - }, - { - "ruleId": "WCAG20_Body_FirstASkips_Native_Host_Sematics", - "value": ["VIOLATION", "FAIL"], - "path": { - "dom": "/html[1]/body[1]", - "aria": "/document[1]" - }, - "ruleTime": 1, - "reasonId": "Fail_1", - "message": "The page is missing a the WAI-ARIA MAIN landmark or a link to skip directly to the main page content as the first link on each page.", - "messageArgs": [], - "apiArgs": [], - "bounds": { - "left": 0, - "top": 0, - "height": 0, - "width": 0 - }, - "snippet": "<body>", - "category": "Accessibility", - "ignored": false, - "level": "violation" - }, - { - "ruleId": "Rpt_Aria_OrphanedContent_Native_Host_Sematics", - "value": ["VIOLATION", "FAIL"], - "path": { - "dom": "/html[1]/body[1]/h1[1]", - "aria": "/document[1]/heading[1]" - }, - "ruleTime": 3, - "reasonId": "Fail_1", - "message": "Orphan content found that does not reside within a WAI-ARIA landmark or labelled region role.", - "messageArgs": [], - "apiArgs": [], - "bounds": { - "left": 0, - "top": 0, - "height": 0, - "width": 0 - }, - "snippet": "<h1>", - "category": "Accessibility", - "ignored": false, - "level": "violation" - }, - { - "ruleId": "WCAG20_Img_HasAlt", - "value": ["VIOLATION", "FAIL"], - "path": { - "dom": "/html[1]/body[1]/img[1]", - "aria": "/document[1]/img[1]" - }, - "ruleTime": 0, - "reasonId": "Fail_2", - "message": "Image is missing alt attribute.", - "messageArgs": [], - "apiArgs": [], - "bounds": { - "left": 0, - "top": 0, - "height": 0, - "width": 0 - }, - "snippet": "<img src=\"blah.jpg\">", - "category": "Accessibility", - "ignored": false, - "level": "violation" - } - ], - "numExecuted": 68, - "nls": { - "WCAG20_Html_HasLang": { - "0": "Page must identify the default language of the document with a lang attribute", - "Fail_3": "Page detected as HTML, but does not have a lang attribute." - }, - "RPT_Html_SkipNav": { - "0": "Provide a mechanism to bypass blocks of content that are repeated on multiple Web pages.", - "Pass_0": "Rule Passed" - }, - "WCAG20_Doc_HasTitle": { - "0": "The page should have a title that correctly identifies the subject of the page.", - "Pass_0": "Rule Passed" - }, - "HAAC_BackgroundImg_HasTextOrTitle": { - "0": "Verify that important background image information is conveyed when the system is in high contrast mode.", - "Pass_0": "Rule Passed" - }, - "RPT_List_UseMarkup": { - "0": "Use proper HTML LIST elements to create lists.", - "Pass_0": "Rule Passed" - }, - "RPT_Text_SensoryReference": { - "0": "Usage of sensory words must be meaningful to users who may not have sensory perception of size, sound, shape, or location.", - "Pass_0": "Rule Passed" - }, - "WCAG20_Text_Emoticons": { - "0": "Provide text alternatives for emoticons.", - "Pass_0": "Rule Passed" - }, - "WCAG20_Text_LetterSpacing": { - "0": "Use CSS 'letter-spacing' to control spacing within a word.", - "Pass_0": "Rule Passed" - }, - "RPT_Blockquote_WrapsTextQuote": { - "0": "Quotations should be marked with Q or BLOCKQUOTE elements.", - "Pass_0": "Rule Passed" - }, - "IBMA_Color_Contrast_WCAG2AA_PV": { - "0": "Contrast of foreground and background must be sufficient to meet WCAG AA requirements.", - "Pass_0": "Rule Passed" - }, - "HAAC_Aria_Native_Host_Sematics": { - "0": "The WAI-ARIA role(s) and/or attribute(s) must be valid for the relevant element.", - "Pass_0": "Rule Passed" - }, - "Rpt_Aria_OrphanedContent_Native_Host_Sematics": { - "0": "All content must reside within a HTML5 Sectioning element, WAI-ARIA landmark or labelled region role.", - "Pass_0": "Rule Passed", - "Fail_1": "Orphan content found that does not reside within a WAI-ARIA landmark or labelled region role." - }, - "RPT_Title_Valid": { - "0": "Provide a descriptive TITLE for Web pages.", - "Pass_0": "Rule Passed" - }, - "WCAG20_Body_FirstASkips_Native_Host_Sematics": { - "0": "The page must provide a link to skip directly to the main page content as the first link on each page or use WAI-ARIA landmarks.", - "Fail_1": "The page is missing a the WAI-ARIA MAIN landmark or a link to skip directly to the main page content as the first link on each page." - }, - "RPT_Header_HasContent": { - "0": "Provide descriptive headings for Web page sections.", - "Pass_0": "Rule Passed" - }, - "IBMA_Color_Contrast_WCAG2AA": { - "0": "Contrast of foreground and background must be sufficient to meet WCAG AA requirements.", - "Pass_0": "Rule Passed" - }, - "RPT_Headers_FewWords": { - "0": "Make sure HEADER elements are used as a heading for a section and not used to format text for presentation.", - "Pass_0": "Rule Passed" - }, - "WCAG20_Img_HasAlt": { - "0": "Images must have an alt attribute, and must be the empty string if decorative.", - "Fail_2": "Image is missing alt attribute." - }, - "RPT_Img_AltCommonMisuse": { - "0": "Ensure that image alt text serves as an inline replacement of the image.", - "Pass_0": "Rule Passed" - } - }, - "summary": { - "counts": { - "violation": 4, - "potentialviolation": 0, - "recommendation": 0, - "potentialrecommendation": 0, - "manual": 0, - "ignored": 0 - }, - "scanTime": 120, - "ruleArchive": "Latest Archive (latest)", - "policies": ["IBM_Accessibility"], - "reportLevels": [ - "violation", - "potentialviolation", - "recommendation", - "potentialrecommendation", - "manual" + "results": [ + { + "ruleId": "WCAG20_Html_HasLang", + "value": [ + "VIOLATION", + "FAIL" + ], + "path": { + "dom": "/html[1]", + "aria": "" + }, + "ruleTime": 0, + "reasonId": "Fail_3", + "message": "Page detected as HTML, but does not have a 'lang' attribute", + "messageArgs": [], + "apiArgs": [], + "bounds": { + "left": 0, + "top": 0, + "height": 660, + "width": 1000 + }, + "snippet": "<html>", + "category": "Accessibility", + "ignored": false, + "level": "violation" + }, + { + "ruleId": "WCAG20_Body_FirstASkips_Native_Host_Sematics", + "value": [ + "VIOLATION", + "FAIL" + ], + "path": { + "dom": "/html[1]/body[1]", + "aria": "/document[1]" + }, + "ruleTime": 0, + "reasonId": "Fail_1", + "message": "The page does not provide a way to quickly navigate to the main content (WAI-ARIA \"main\" landmark or a skip link)", + "messageArgs": [], + "apiArgs": [], + "bounds": { + "left": 8, + "top": 8, + "height": 644, + "width": 984 + }, + "snippet": "<body>", + "category": "Accessibility", + "ignored": false, + "level": "violation" + }, + { + "ruleId": "Rpt_Aria_OrphanedContent_Native_Host_Sematics", + "value": [ + "VIOLATION", + "FAIL" + ], + "path": { + "dom": "/html[1]/body[1]/h1[1]", + "aria": "/document[1]/heading[1]" + }, + "ruleTime": 0, + "reasonId": "Fail_1", + "message": "Content is not within a landmark element", + "messageArgs": [], + "apiArgs": [], + "bounds": { + "left": 8, + "top": 8, + "height": 37, + "width": 984 + }, + "snippet": "<h1>", + "category": "Accessibility", + "ignored": false, + "level": "violation" + }, + { + "ruleId": "WCAG20_Img_HasAlt", + "value": [ + "VIOLATION", + "FAIL" + ], + "path": { + "dom": "/html[1]/body[1]/img[1]", + "aria": "/document[1]/img[1]" + }, + "ruleTime": 0, + "reasonId": "Fail_2", + "message": "Image does not have an 'alt' attribute short text alternative", + "messageArgs": [], + "apiArgs": [], + "bounds": { + "left": 8, + "top": 67, + "height": 16, + "width": 16 + }, + "snippet": "<img src=\"missing-alt.jpg\">", + "category": "Accessibility", + "ignored": false, + "level": "violation" + } ], - "startScan": 1588966929821, - "URL": "about:blank" - }, - "scanID": "30f701d0-5283-46ed-a653-7a3787a8d27d", - "toolID": "accessibility-checker-v3.0.0-beta.2", - "label": "assert compliance rc 0 no baseline" -} + "numExecuted": 68, + "nls": { + "WCAG20_Html_HasLang": { + "0": "Page must identify the default language of the document with a 'lang' attribute", + "Fail_3": "Page detected as HTML, but does not have a 'lang' attribute" + }, + "RPT_Html_SkipNav": { + "0": "Provide a way to bypass blocks of content that are repeated on multiple Web pages", + "Pass_0": "Rule Passed" + }, + "WCAG20_Doc_HasTitle": { + "0": "The page should have a title that correctly identifies the subject of the page", + "Pass_0": "Rule Passed" + }, + "HAAC_BackgroundImg_HasTextOrTitle": { + "0": "Background images that convey important information must have a text alternative that describes the image", + "Pass_0": "Rule Passed" + }, + "RPT_List_UseMarkup": { + "0": "Use proper HTML list elements to create lists", + "Pass_0": "Rule Passed" + }, + "RPT_Text_SensoryReference": { + "0": "Instructions must be meaningful without shape or location words", + "Pass_0": "Rule Passed" + }, + "WCAG20_Text_Emoticons": { + "0": "Emoticons must have a short text alternative that describes their purpose", + "Pass_0": "Rule Passed" + }, + "WCAG20_Text_LetterSpacing": { + "0": "Use CSS 'letter-spacing' to control spacing within a word", + "Pass_0": "Rule Passed" + }, + "RPT_Blockquote_WrapsTextQuote": { + "0": "Quotations should be marked with <q> or <blockquote> elements", + "Pass_0": "Rule Passed" + }, + "IBMA_Color_Contrast_WCAG2AA_PV": { + "0": "The contrast ratio of text with its background (i.e. background with a color gradient or a background image) must meet WCAG 2.1 AA requirements", + "Pass_0": "Rule Passed" + }, + "HAAC_Aria_Native_Host_Sematics": { + "0": "WAI-ARIA roles and attributes must be valid for the element they are assigned to", + "Pass_0": "Rule Passed" + }, + "Rpt_Aria_OrphanedContent_Native_Host_Sematics": { + "0": "All content must reside within an element with a landmark role", + "Pass_0": "Rule Passed", + "Fail_1": "Content is not within a landmark element" + }, + "RPT_Title_Valid": { + "0": "Page <title> should be a descriptive title, rather than a filename", + "Pass_0": "Rule Passed" + }, + "WCAG20_Body_FirstASkips_Native_Host_Sematics": { + "0": "Pages must provide a way to skip directly to the main content", + "Fail_1": "The page does not provide a way to quickly navigate to the main content (WAI-ARIA \"main\" landmark or a skip link)" + }, + "RPT_Header_HasContent": { + "0": "Heading elements must provide descriptive text", + "Pass_0": "Rule Passed" + }, + "IBMA_Color_Contrast_WCAG2AA": { + "0": "The contrast ratio of text with its background must meet WCAG 2.1 AA requirements", + "Pass_0": "Rule Passed" + }, + "RPT_Headers_FewWords": { + "0": "Heading elements must not be used for presentation", + "Pass_0": "Rule Passed" + }, + "WCAG20_Img_HasAlt": { + "0": "Images must have an 'alt' attribute with a short text alternative if they convey meaning, or 'alt=\"\" if decorative", + "Fail_2": "Image does not have an 'alt' attribute short text alternative" + }, + "RPT_Img_AltCommonMisuse": { + "0": "'alt' attribute value must be a good inline replacement for the image", + "Pass_0": "Rule Passed" + } + }, + "summary": { + "counts": { + "violation": 4, + "potentialviolation": 0, + "ignored": 0 + }, + "scanTime": 9, + "ruleArchive": "Latest Deployment (latest)", + "policies": [ + "IBM_Accessibility" + ], + "reportLevels": [ + "violation", + "potentialviolation" + ], + "startScan": 1601305146918, + "URL": "http://localhost:8080/test/sample-html/violations.html" + }, + "scanID": "25bdec16-c677-4773-afdb-56319dffc0b7", + "toolID": "cypress-accessibility-checker-v3.0.0", + "label": "violations" +} \ No newline at end of file diff --git a/cypress-accessibility-checker/test/cypress/fixtures/example.json b/cypress-accessibility-checker/test/cypress/fixtures/example.json new file mode 100644 index 000000000..da18d9352 --- /dev/null +++ b/cypress-accessibility-checker/test/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} \ No newline at end of file diff --git a/cypress-accessibility-checker/test/cypress/integration/achecker.js b/cypress-accessibility-checker/test/cypress/integration/achecker.js index b6cff425e..8095e1d37 100644 --- a/cypress-accessibility-checker/test/cypress/integration/achecker.js +++ b/cypress-accessibility-checker/test/cypress/integration/achecker.js @@ -17,127 +17,109 @@ /// <reference types="cypress" /> context('Accessibility checker tests', () => { - it('getA11yCompliance() returns a report of the sent HTML', () => { - const html = ` - <html> - <head> - <title>Test - - -

Hello world

- - - `; - cy.visit('no-violations.html') - .getA11yCompliance(html, 'getCompliance fake html') - .then((result) => { - expect(result.report.results).to.have.length.greaterThan(0); - }); - }); - - it('getA11yComplianceOfDocument() returns a report of the document', () => { - cy.visit('no-violations.html') - .getA11yComplianceOfDocument('getComplianceOfDocument no violations') - .then((result) => { - console.warn(result); - expect(result.report.results).to.have.lengthOf(0); - }); - - cy.visit('violations.html') - .getA11yComplianceOfDocument('getComplianceOfDocument with violations') - .then((result) => { - console.warn(result); - expect(result.report.results).to.have.length.greaterThan(0); - }); - }); - - context('assertA11yCompliance()', () => { - it('Is successful when there are no violations', () => { - cy.visit('no-violations.html') - .getA11yComplianceOfDocument('assert compliance rc 0 no baseline') - .assertA11yCompliance() - .then((rc) => expect(rc).to.equal(0)); + + it('getCompliance() returns a report of the document', () => { + cy.visit('no-violations.html') + .getCompliance('getComplianceOfDocument no violations') + .then((report) => { + console.warn(report); + expect(report.results).to.have.lengthOf(0); + }); + + cy.visit('violations.html') + .getCompliance('getComplianceOfDocument with violations') + .then((report) => { + console.warn(report); + expect(report.results).to.have.length.greaterThan(0); + }); + }); + context('assertCompliance()', () => { + it('Is successful when there are no violations', () => { + cy.visit('no-violations.html') + .getCompliance('assert compliance rc 0 no baseline') + .assertCompliance() + .then((rc) => { + return expect(rc).to.equal(0) + }); + }); + + it('Is successful when the baselines match', () => { + cy.visit('violations.html') + .getCompliance('violations') + .assertCompliance(false) + .then((rc) => expect(rc).to.equal(0)); + }); + + it('Fails when the baselines dont match', () => { + // Compare no-violations to a violations baseline + cy.visit('violations.html') + .getCompliance('violations-no-match') + .assertCompliance(false) + .then((rc) => { + expect(rc).to.equal(1); + }) + }); + + it('Fails when there are violations due to fail levels', () => { + cy.visit('violations.html') + .getCompliance('assert compliance rc 2') + .assertCompliance(false) // Don't actually run the assertion in the command so we can check the output + .then((rc) => expect(rc).to.equal(2)); + }); + }); + + it('getBaseline() should return data from baseline scan', () => { + cy.visit('violations.html').getCompliance('getBaseline test'); + + cy.getBaseline('getBaseline test').then( + (result) => expect(result).not.to.be.null + ); }); - it('Is successful when the baselines match', () => { - cy.visit('violations.html') - .getA11yComplianceOfDocument('violations') - .assertA11yCompliance(false) - .then((rc) => expect(rc).to.equal(0)); + it('getDiffResults() should return diff between scan and baseline', () => { + // Compare violations to a no-violations baseline + cy.visit('violations.html') + .getCompliance('violations-no-match') + .assertCompliance(false) + .then((rc) => { + expect(rc).to.equal(1); + }) + + cy.getDiffResults('violations-no-match').then((result) => { + expect(result).not.to.be.null; + result.forEach((obj) => expect(obj.kind).not.to.be.null); // Check object is what we expect + result.forEach((obj) => expect(obj.kind).not.to.be.undefined); + }); }); - it('Fails when the baselines dont match', () => { - // Compare no-violations to a violations baseline - cy.visit('no-violations.html') - .getA11yComplianceOfDocument('violations') - .assertA11yCompliance(false) - .then((rc) => expect(rc).to.equal(1)); + it('diffResultsWithExpected() should return a diff between actual and expected', () => { + cy.visit('violations.html') + .getCompliance('diff accessibility results with expected 1') + .then((actual) => { + cy.getBaseline('violations') + .then((expected) => { + return cy.diffResultsWithExpected( + actual, + expected, + true + ); + }) + .then((result) => { + // Check to see if the object has some expected properties + result.forEach((obj) => expect(obj.kind).not.to.be.null); // Check object is what we expect + result.forEach((obj) => expect(obj.kind).not.to.be.undefined); + }); + }); }); - it('Fails when there are violations due to fail levels', () => { - cy.visit('violations.html') - .getA11yComplianceOfDocument('assert compliance rc 2') - .assertA11yCompliance(false) // Don't actually run the assertion in the command so we can check the output - .then((rc) => expect(rc).to.equal(2)); + it('stringifyResults() should return stringified version of report', () => { + cy.visit('violations.html') + .getCompliance('stringify results 1') + .stringifyResults() + .then((stringResult) => expect(stringResult).to.contain('Scan:')); }); - }); - - it('getA11yBaseline() should return data from baseline scan', () => { - cy.visit('violations.html').getA11yComplianceOfDocument('getBaseline test'); - - cy.getA11yBaseline('violations').then( - (result) => expect(result).not.to.be.null - ); - cy.getA11yBaseline('no-violations').then( - (result) => expect(result).not.to.be.null - ); - }); - - it('getA11yDiffResults() should return diff between scan and baseline', () => { - // Compare violations to a no-violations baseline - cy.visit('violations.html') - .getA11yComplianceOfDocument('no-violations') - .assertA11yCompliance(false) - .then((rc) => expect(rc).to.be.equal(1)); - - cy.getA11yDiffResults('no-violations').then((result) => { - expect(result).not.to.be.null; - result.forEach((obj) => expect(obj.item).not.to.be.null); // Check object is what we expect + + it('getACheckerConfig() should return config block from config file', () => { + cy.getACheckerConfig().then((config) => expect(config.toolID).not.to.be.null); }); - }); - - it('diffA11yResultsWithExpected() should return a diff between actual and expected', () => { - cy.visit('violations.html') - .getA11yComplianceOfDocument('diff a11y results with expected 1') - .then((actual) => { - cy.getA11yBaseline('violations') - .then((expected) => { - return cy.diffA11yResultsWithExpected( - actual.report, - expected, - true - ); - }) - .then((result) => { - // Check to see if the object has some expected properties - expect(result.path).not.to.be.null; - }); - }); - }); - - it('stringifyA11yResults() should return stringified version of report', () => { - cy.visit('violations.html') - .getA11yComplianceOfDocument('stringify results 1') - .stringifyA11yResults() - .then((stringResult) => expect(stringResult).to.contain('Scan:')); - }); - - it('getA11yConfig() should return config block from config file', () => { - cy.getA11yConfig().then((config) => expect(config.toolID).not.to.be.null); - }); - - it('closeA11y() should free up resources', () => { - // Hard to really test this, but make sure it doesn't break when calling it - cy.closeA11y(); - }); }); diff --git a/cypress-accessibility-checker/test/cypress/plugins/index.js b/cypress-accessibility-checker/test/cypress/plugins/index.js index 8aac1cf5e..b9b9b2262 100644 --- a/cypress-accessibility-checker/test/cypress/plugins/index.js +++ b/cypress-accessibility-checker/test/cypress/plugins/index.js @@ -30,6 +30,7 @@ const accessibilityCheckerTasks = require('../../../plugin'); module.exports = (on /*, config*/) => { + console.log("LOADING PLUGINS"); on('task', { accessibilityChecker: accessibilityCheckerTasks });