diff --git a/lib/core/public/load.js b/lib/core/public/load.js index 124c849b..9595f3a9 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 3acb77fa..e21a0b8a 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 00000000..7fffe01e --- /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;