From 6dc8ff65458b6d822e868703ce9b446059c2d732 Mon Sep 17 00:00:00 2001 From: Abhishek Bindra Date: Wed, 10 Jul 2024 13:02:55 +0530 Subject: [PATCH 1/8] support advanced rule execution for iframes --- lib/core/public/run-rules.js | 7 +++++++ lib/core/utils/collect-results-from-frames.js | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/lib/core/public/run-rules.js b/lib/core/public/run-rules.js index 5eb26d7c9..aa5d2439c 100644 --- a/lib/core/public/run-rules.js +++ b/lib/core/public/run-rules.js @@ -1,3 +1,4 @@ +/*global a11yEngine*/ import Context from '../base/context'; import teardown from './teardown'; import { @@ -36,6 +37,12 @@ export default function runRules(context, options, resolve, reject) { performanceTimer.auditStart(); } + if (options.a11yEngineConfig && options.a11yEngineConfig.advancedRun) { + // do a11yEngine setup for iframe + console.log('>>> iframe detected, initiating setup ...'); + a11yEngine.setup(options.a11yEngineConfig); + } + if (context.frames.length && options.iframes !== false) { q.defer((res, rej) => { collectResultsFromFrames(context, options, 'rules', null, res, rej); diff --git a/lib/core/utils/collect-results-from-frames.js b/lib/core/utils/collect-results-from-frames.js index be17f8ccf..58f829002 100644 --- a/lib/core/utils/collect-results-from-frames.js +++ b/lib/core/utils/collect-results-from-frames.js @@ -22,6 +22,11 @@ export default function collectResultsFromFrames( // elementRefs can't be passed across frame boundaries options = { ...options, elementRef: false }; + // check a11yengine advance run flag + if (options.a11yEngineConfig && !options.a11yEngineConfig.advancedRun) { + options.a11yEngineConfig.advancedRun = true; + } + var q = queue(); var frames = parentContent.frames; From 8f84c27622b2c85b52e421b24e67ea4bd81b54b7 Mon Sep 17 00:00:00 2001 From: Utkarsh Chaudhary Date: Tue, 16 Jul 2024 17:49:59 +0530 Subject: [PATCH 2/8] Removed console --- lib/core/public/run-rules.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/core/public/run-rules.js b/lib/core/public/run-rules.js index aa5d2439c..3acb77fab 100644 --- a/lib/core/public/run-rules.js +++ b/lib/core/public/run-rules.js @@ -38,8 +38,6 @@ export default function runRules(context, options, resolve, reject) { } if (options.a11yEngineConfig && options.a11yEngineConfig.advancedRun) { - // do a11yEngine setup for iframe - console.log('>>> iframe detected, initiating setup ...'); a11yEngine.setup(options.a11yEngineConfig); } From 10bd5a0c5bd3d1b330092e79b7ce98da2ac5e1e2 Mon Sep 17 00:00:00 2001 From: Suyash Sonawane Date: Tue, 30 Jul 2024 14:07:55 +0530 Subject: [PATCH 3/8] adding custom stub --- Gruntfile.js | 8 +++++--- build/tasks/aria-supported.js | 3 ++- lib/custom/intro.stub | 15 +++++++++++++++ lib/custom/outro.stub | 2 ++ package.json | 1 + 5 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 lib/custom/intro.stub create mode 100644 lib/custom/outro.stub diff --git a/Gruntfile.js b/Gruntfile.js index 0a888fe1c..47d9e0787 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -15,6 +15,7 @@ module.exports = function (grunt) { grunt.loadTasks('build/tasks'); var langs; + var wrapper = !!grunt.option('wrapper'); if (grunt.option('lang')) { langs = (grunt.option('lang') || '').split(/[,;]/g).map(function (lang) { lang = lang.trim(); @@ -85,11 +86,11 @@ module.exports = function (grunt) { files: langs.map(function (lang, i) { return { src: [ - 'lib/intro.stub', + wrapper ? 'lib/custom/intro.stub' : 'lib/intro.stub', '<%= concat.engine.coreFiles %>', // include rules / checks / commons '<%= configure.rules.files[' + i + '].dest.auto %>', - 'lib/outro.stub' + wrapper ? 'lib/custom/outro.stub' : 'lib/outro.stub' ], dest: 'axe' + lang + '.js' }; @@ -127,7 +128,8 @@ module.exports = function (grunt) { entry: 'lib/commons/aria/index.js', destFile: 'doc/aria-supported.md', options: { - langs: langs + langs: langs, + wrapper: wrapper }, listType: 'unsupported' // Possible values for listType: 'supported', 'unsupported', 'all' } diff --git a/build/tasks/aria-supported.js b/build/tasks/aria-supported.js index 257b52ed5..efe43437f 100644 --- a/build/tasks/aria-supported.js +++ b/build/tasks/aria-supported.js @@ -16,7 +16,8 @@ module.exports = function (grunt) { * hence cannot be required at the top of the file. */ const done = this.async(); - const { langs } = this.options(); + const { langs, wrapper } = this.options(); + if (wrapper) return true; const fileNameSuffix = langs && langs.length > 0 ? `${langs[0]}` : ''; const axe = require(`../../axe${fileNameSuffix}`); const listType = this.data.listType.toLowerCase(); diff --git a/lib/custom/intro.stub b/lib/custom/intro.stub new file mode 100644 index 000000000..9ed8b3c4b --- /dev/null +++ b/lib/custom/intro.stub @@ -0,0 +1,15 @@ +/*! axe v<%= pkg.version %> + * Copyright (c) 2015 - <%= grunt.template.today("yyyy") %> Deque Systems, Inc. + * + * Your use of this Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This entire copyright notice must appear in every copy of this file you + * distribute or in any file that contains substantial portions of this source + * code. + */ +const createAxe = () => (function axeFunction (window) { + // A window reference is required to access the axe object in a "global". + var global = window; + var document = window.document; diff --git a/lib/custom/outro.stub b/lib/custom/outro.stub new file mode 100644 index 000000000..19fd22a6e --- /dev/null +++ b/lib/custom/outro.stub @@ -0,0 +1,2 @@ + +}( typeof window === 'object' ? window : this )); diff --git a/package.json b/package.json index 6c9a3e1fa..5d5beb356 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "develop": "grunt dev --force", "api-docs": "jsdoc --configure .jsdoc.json", "build": "grunt", + "build:wrapper": "grunt --wrapper=true", "eslint": "eslint --color --format stylish '{lib,test,build,doc}/**/*.js' 'Gruntfile.js'", "test": "npm run test:tsc && run-s \"test:unit:* -- {@}\" --", "test:tsc": "tsc", From c7b3fa4ebd30d16d62fd8e25600505de4a980c86 Mon Sep 17 00:00:00 2001 From: Utkarsh Chaudhary Date: Fri, 2 Aug 2024 09:04:23 +0530 Subject: [PATCH 4/8] Iframe error merging logic added --- lib/core/public/load.js | 17 +++++++++++ lib/core/public/run-rules.js | 9 +++++- lib/core/utils/merge-errors.js | 52 ++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 lib/core/utils/merge-errors.js diff --git a/lib/core/public/load.js b/lib/core/public/load.js index 124c849b1..9595f3a99 100644 --- a/lib/core/public/load.js +++ b/lib/core/public/load.js @@ -1,8 +1,10 @@ +/*global a11yEngine*/ import Audit from '../base/audit'; import cleanup from './cleanup'; import runRules from './run-rules'; import respondable from '../utils/respondable'; import nodeSerializer from '../utils/node-serializer'; +import mergeErrors from '../utils/merge-errors'; /** * Sets up Rules, Messages and default options for Checks, must be invoked before attempting analysis @@ -36,6 +38,21 @@ function runCommand(data, keepalive, callback) { (results, cleanupFn) => { // Serialize all DqElements results = nodeSerializer.mapRawResults(results); + + //a11y-engine iframe rules error merging logic + const errors = a11yEngine.getErrors(); + if (Object.keys(errors).length !== 0) { + if (results[results.length - 1].errorsObj) { + const error = results.pop(); + delete error.errorsObj; + const mergedErrors = mergeErrors(error, errors); + results.push({ ...mergedErrors, errorsObj: true }); + } else { + results.push({ ...errors, errorsObj: true }); + } + } + a11yEngine.clearErrors(); + resolve(results); // Cleanup AFTER resolve so that selectors can be generated cleanupFn(); diff --git a/lib/core/public/run-rules.js b/lib/core/public/run-rules.js index 3acb77fab..e21a0b8a1 100644 --- a/lib/core/public/run-rules.js +++ b/lib/core/public/run-rules.js @@ -41,7 +41,7 @@ export default function runRules(context, options, resolve, reject) { a11yEngine.setup(options.a11yEngineConfig); } - if (context.frames.length && options.iframes !== false) { + if (context.frames.length && options.iframes === true) { q.defer((res, rej) => { collectResultsFromFrames(context, options, 'rules', null, res, rej); }); @@ -64,6 +64,13 @@ export default function runRules(context, options, resolve, reject) { // after should only run once, so ensure we are in the top level window if (context.initiator) { + // Return a11y-engine errors when at top level window + if (results[results.length - 1].errorsObj) { + const error = results.pop(); + delete error.errorsObj; + a11yEngine.mergeErrors(error); + } + results = audit.after(results, options); results.forEach(publishMetaData); diff --git a/lib/core/utils/merge-errors.js b/lib/core/utils/merge-errors.js new file mode 100644 index 000000000..7fffe01eb --- /dev/null +++ b/lib/core/utils/merge-errors.js @@ -0,0 +1,52 @@ +// Function to merge errors for a11y-engine. +// Handles errors differently for check_errors and other errors. +// It also adds the target selector to the errors for better identification. + +function mergeErrors(mergedErrors, frameErrors, frameSpec) { + for (const [key, value] of Object.entries(frameErrors)) { + if (key === 'check_errors') { + if (!mergedErrors[key]) { + mergedErrors[key] = {}; + } + + for (const [checkNameKey, checkNameValue] of Object.entries(value)) { + // Add the target if not present. If present then append parents target. + checkNameValue.forEach(checkNameValueError => { + if (!checkNameValueError.target && frameSpec) { + checkNameValueError.target = frameSpec?.selector; + } else if (checkNameValueError.target && frameSpec) { + checkNameValueError.target = [ + ...frameSpec.selector, + ...checkNameValueError.target + ]; + } + }); + if (mergedErrors[key][checkNameKey]) { + mergedErrors[key][checkNameKey].push(...checkNameValue); + } else { + mergedErrors[key][checkNameKey] = Array.isArray(checkNameValue) + ? [...checkNameValue] + : [checkNameValue]; + } + } + } else { + // Add the target if not present. If present then append parents target. + value.forEach(errorValue => { + if (!errorValue.target && frameSpec) { + errorValue.target = frameSpec?.selector; + } else if (errorValue.target && frameSpec) { + errorValue.target = [...frameSpec.selector, ...errorValue.target]; + } + }); + if (mergedErrors[key]) { + mergedErrors[key] = [...mergedErrors[key], ...value]; + } else { + mergedErrors[key] = value; + } + } + } + + return mergedErrors; +} + +export default mergeErrors; From 2e22663843643c8312bf0a86a1315bb03247fc82 Mon Sep 17 00:00:00 2001 From: Utkarsh Chaudhary Date: Fri, 2 Aug 2024 09:18:38 +0530 Subject: [PATCH 5/8] Iframe error merging logic added --- lib/core/utils/merge-results.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/core/utils/merge-results.js b/lib/core/utils/merge-results.js index e5c9547ad..b76cf5b55 100644 --- a/lib/core/utils/merge-results.js +++ b/lib/core/utils/merge-results.js @@ -1,6 +1,7 @@ import nodeSerializer from './node-serializer'; import getAllChecks from './get-all-checks'; import findBy from './find-by'; +import mergeErrors from './merge-errors'; /** * Adds the owning frame's CSS selector onto each instance of DqElement @@ -75,6 +76,7 @@ function normalizeResult(result) { */ function mergeResults(frameResults, options) { const mergedResult = []; + let mergedErrors = {}; frameResults.forEach(frameResult => { const results = normalizeResult(frameResult); if (!results || !results.length) { @@ -82,6 +84,12 @@ function mergeResults(frameResults, options) { } const frameSpec = getFrameSpec(frameResult); + if (results[results.length - 1].errorsObj) { + const error = results.pop(); + delete error.errorsObj; + mergedErrors = mergeErrors(mergedErrors, error, frameSpec); + } + results.forEach(ruleResult => { if (ruleResult.nodes && frameSpec) { pushFrame(ruleResult.nodes, options, frameSpec); @@ -106,7 +114,11 @@ function mergeResults(frameResults, options) { }); } }); - return mergedResult; + + if (Object.keys(mergedErrors).length === 0) { + return mergedResult; + } + return [...mergedResult, { ...mergedErrors, errorsObj: true }]; } function nodeIndexSort(nodeIndexesA = [], nodeIndexesB = []) { From 0b2e1ff2f46d8957a5255957c48e92f332eb8455 Mon Sep 17 00:00:00 2001 From: Utkarsh Chaudhary Date: Fri, 2 Aug 2024 14:32:54 +0530 Subject: [PATCH 6/8] Addressed comments --- lib/core/public/load.js | 8 ++++---- lib/core/public/run-rules.js | 4 ++-- lib/core/utils/merge-results.js | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/core/public/load.js b/lib/core/public/load.js index 9595f3a99..e80d3d8f6 100644 --- a/lib/core/public/load.js +++ b/lib/core/public/load.js @@ -42,13 +42,13 @@ function runCommand(data, keepalive, callback) { //a11y-engine iframe rules error merging logic const errors = a11yEngine.getErrors(); if (Object.keys(errors).length !== 0) { - if (results[results.length - 1].errorsObj) { + if (results[results.length - 1].a11yEngineErrors) { const error = results.pop(); - delete error.errorsObj; + delete error.a11yEngineErrors; const mergedErrors = mergeErrors(error, errors); - results.push({ ...mergedErrors, errorsObj: true }); + results.push({ ...mergedErrors, a11yEngineErrors: true }); } else { - results.push({ ...errors, errorsObj: true }); + results.push({ ...errors, a11yEngineErrors: true }); } } a11yEngine.clearErrors(); diff --git a/lib/core/public/run-rules.js b/lib/core/public/run-rules.js index e21a0b8a1..effca205d 100644 --- a/lib/core/public/run-rules.js +++ b/lib/core/public/run-rules.js @@ -65,9 +65,9 @@ export default function runRules(context, options, resolve, reject) { // after should only run once, so ensure we are in the top level window if (context.initiator) { // Return a11y-engine errors when at top level window - if (results[results.length - 1].errorsObj) { + if (results[results.length - 1].a11yEngineErrors) { const error = results.pop(); - delete error.errorsObj; + delete error.a11yEngineErrors; a11yEngine.mergeErrors(error); } diff --git a/lib/core/utils/merge-results.js b/lib/core/utils/merge-results.js index b76cf5b55..f6cf761b3 100644 --- a/lib/core/utils/merge-results.js +++ b/lib/core/utils/merge-results.js @@ -84,9 +84,9 @@ function mergeResults(frameResults, options) { } const frameSpec = getFrameSpec(frameResult); - if (results[results.length - 1].errorsObj) { + if (results[results.length - 1].a11yEngineErrors) { const error = results.pop(); - delete error.errorsObj; + delete error.a11yEngineErrors; mergedErrors = mergeErrors(mergedErrors, error, frameSpec); } @@ -118,7 +118,7 @@ function mergeResults(frameResults, options) { if (Object.keys(mergedErrors).length === 0) { return mergedResult; } - return [...mergedResult, { ...mergedErrors, errorsObj: true }]; + return [...mergedResult, { ...mergedErrors, a11yEngineErrors: true }]; } function nodeIndexSort(nodeIndexesA = [], nodeIndexesB = []) { From 5820a5938c3467ccc5a53b3ba17438dae22b45db Mon Sep 17 00:00:00 2001 From: Utkarsh Chaudhary Date: Mon, 5 Aug 2024 07:42:50 +0530 Subject: [PATCH 7/8] Addressed comments --- lib/core/public/run-rules.js | 4 +++- lib/core/utils/collect-results-from-frames.js | 9 ++++++--- lib/core/utils/merge-results.js | 2 ++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/core/public/run-rules.js b/lib/core/public/run-rules.js index effca205d..7ae0b92b9 100644 --- a/lib/core/public/run-rules.js +++ b/lib/core/public/run-rules.js @@ -37,10 +37,12 @@ export default function runRules(context, options, resolve, reject) { performanceTimer.auditStart(); } - if (options.a11yEngineConfig && options.a11yEngineConfig.advancedRun) { + // If advanced run for iframes is true then setup socket for iframes + if (options.a11yEngineConfig && options.a11yEngineConfig.iframesAdvancedRun) { a11yEngine.setup(options.a11yEngineConfig); } + // If run for iframes is true then collect results from iframes if (context.frames.length && options.iframes === true) { q.defer((res, rej) => { collectResultsFromFrames(context, options, 'rules', null, res, rej); diff --git a/lib/core/utils/collect-results-from-frames.js b/lib/core/utils/collect-results-from-frames.js index 58f829002..e19e356a8 100644 --- a/lib/core/utils/collect-results-from-frames.js +++ b/lib/core/utils/collect-results-from-frames.js @@ -22,9 +22,12 @@ export default function collectResultsFromFrames( // elementRefs can't be passed across frame boundaries options = { ...options, elementRef: false }; - // check a11yengine advance run flag - if (options.a11yEngineConfig && !options.a11yEngineConfig.advancedRun) { - options.a11yEngineConfig.advancedRun = true; + // check a11yengine iframe advance run flag + if ( + options.a11yEngineConfig && + options.a11yEngineConfig.iframesAdvancedRun === false + ) { + options.a11yEngineConfig.iframesAdvancedRun = true; } var q = queue(); diff --git a/lib/core/utils/merge-results.js b/lib/core/utils/merge-results.js index f6cf761b3..0e6277132 100644 --- a/lib/core/utils/merge-results.js +++ b/lib/core/utils/merge-results.js @@ -76,6 +76,7 @@ function normalizeResult(result) { */ function mergeResults(frameResults, options) { const mergedResult = []; + // A11yEngine merged errors let mergedErrors = {}; frameResults.forEach(frameResult => { const results = normalizeResult(frameResult); @@ -84,6 +85,7 @@ function mergeResults(frameResults, options) { } const frameSpec = getFrameSpec(frameResult); + // Extract existing errors and merge with new ones if (results[results.length - 1].a11yEngineErrors) { const error = results.pop(); delete error.a11yEngineErrors; From 2dbf6fbdbe5e74bff7c121200a106d0f5dd87f21 Mon Sep 17 00:00:00 2001 From: Utkarsh Chaudhary Date: Tue, 6 Aug 2024 16:02:46 +0530 Subject: [PATCH 8/8] Iframe fix for automate --- lib/core/public/run-rules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/public/run-rules.js b/lib/core/public/run-rules.js index 7ae0b92b9..8e04c13dc 100644 --- a/lib/core/public/run-rules.js +++ b/lib/core/public/run-rules.js @@ -43,7 +43,7 @@ export default function runRules(context, options, resolve, reject) { } // If run for iframes is true then collect results from iframes - if (context.frames.length && options.iframes === true) { + if (context.frames.length && options.iframes !== false) { q.defer((res, rej) => { collectResultsFromFrames(context, options, 'rules', null, res, rej); });