Skip to content

Commit

Permalink
Show Purple AI, xpath and custom flow screenshots in report and updat…
Browse files Browse the repository at this point in the history
…e descriptions for axe core rules (#202)

* Group and display issues by screenshots for custom flow scans

* Add lightbox for custom flow screenshots

* Include XPath as part of JSON and HTML report

* Show Purple AI when there is nointernet connection

* Enable copy xpath to clipboard

* Hide Purple AI feedback when there is no internet connection

* Styling changes and add tooltip for copy to clipboard feature

* Detect network error and display error message for AI feature in report

* Fix tooltip to display copied state on click

* Styling change for custom flow screenshot lightbox

* Fix copy button state change

* Escape white space characters in arg

* Update rule descriptions for new axe core rules

* Resolve merge conflict

* Hide a11y online banner when it is unreachable

* Revert to prod google forms
  • Loading branch information
greyguy21 authored Oct 19, 2023
1 parent ff554be commit 4fc171b
Show file tree
Hide file tree
Showing 14 changed files with 555 additions and 252 deletions.
5 changes: 3 additions & 2 deletions constants/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const blackListedFileExtensions = [
];

export const getIntermediateScreenshotsPath = datasetsPath => `${datasetsPath}/screenshots`;
export const destinationPath = storagePath => `${storagePath}/screenshots`;
export const destinationPath = storagePath => `${storagePath}/reports/screenshots`;

/** Get the path to Default Profile in the Chrome Data Directory
* as per https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md
Expand Down Expand Up @@ -229,7 +229,8 @@ export const impactOrder = {
};

export const formDataFields = {
formUrl: `https://docs.google.com/forms/d/e/1FAIpQLSem5C8fyNs5TiU5Vv2Y63-SH7CHN86f-LEPxeN_1u_ldUbgUA/formResponse`,
formUrl: `https://docs.google.com/forms/d/e/1FAIpQLSem5C8fyNs5TiU5Vv2Y63-SH7CHN86f-LEPxeN_1u_ldUbgUA/formResponse`, // prod
// formUrl: `https://docs.google.com/forms/d/e/1FAIpQLScNldkNEajZbAiXK5TmMy4DfMERC2Sd7aJJrD76vBNz4pm05g/formResponse`, // dev
websiteUrlField: 'entry.1562345227',
scanTypeField: 'entry.1148680657',
emailField: 'entry.52161304',
Expand Down
17 changes: 11 additions & 6 deletions crawlers/commonCrawlerFunc.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { guiInfoLog } from '../logs.js';
import { takeScreenshotForHTMLElements } from '../screenshotFunc/htmlScreenshotFunc.js';
import fs from 'fs';

export const filterAxeResults = (needsReview, results, pageTitle) => {
export const filterAxeResults = (needsReview, results, pageTitle, customFlowDetails) => {
const { violations, passes, incomplete, url } = results;

let totalItems = 0;
Expand Down Expand Up @@ -36,7 +36,7 @@ export const filterAxeResults = (needsReview, results, pageTitle) => {
}

const addTo = (category, node) => {
const { html, failureSummary, screenshotPath } = node;
const { html, failureSummary, screenshotPath, target } = node;
if (!(rule in category.rules)) {
category.rules[rule] = { description, helpUrl, conformance, totalItems: 0, items: [] };
}
Expand All @@ -49,12 +49,15 @@ export const filterAxeResults = (needsReview, results, pageTitle) => {
finalHtml = html.replaceAll('</script>', '&lt;/script>');
}

const xpath = target.length === 1 && typeof target[0] === 'string' ? target[0] : null;

// add in screenshot path
category.rules[rule].items.push(
{
html: finalHtml,
html: finalHtml,
message,
screenshotPath,
...(xpath && { xpath }),
...(displayNeedsReview && { displayNeedsReview })
}
);
Expand Down Expand Up @@ -99,15 +102,17 @@ export const filterAxeResults = (needsReview, results, pageTitle) => {

return {
url,
pageTitle,
pageTitle: customFlowDetails ? `${customFlowDetails.pageIndex}: ${pageTitle}` : pageTitle,
...(customFlowDetails && { pageIndex: customFlowDetails.pageIndex }),
...(customFlowDetails && { pageImagePath: customFlowDetails.pageImagePath }),
totalItems,
mustFix,
goodToFix,
passed,
};
};

export const runAxeScript = async (needsReview, includeScreenshots, page, randomToken, selectors = []) => {
export const runAxeScript = async (needsReview, includeScreenshots, page, randomToken, customFlowDetails, selectors = []) => {
await crawlee.playwrightUtils.injectFile(page, axeScript);

const results = await page.evaluate(
Expand Down Expand Up @@ -138,7 +143,7 @@ export const runAxeScript = async (needsReview, includeScreenshots, page, random
}

const pageTitle = await page.evaluate(() => document.title);
return filterAxeResults(needsReview, results, pageTitle);
return filterAxeResults(needsReview, results, pageTitle, customFlowDetails);
};

export const createCrawleeSubFolders = async randomToken => {
Expand Down
70 changes: 43 additions & 27 deletions mergeAxeResults.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ const writeSummaryPdf = async (htmlFilePath, fileDestinationPath) => {
fs.unlinkSync(htmlFilePath);
};

const pushResults = async (pageResults, allIssues) => {
const pushResults = async (pageResults, allIssues, isCustomFlow) => {
const { url, pageTitle, filePath } = pageResults;

allIssues.totalPagesScanned += 1;
Expand Down Expand Up @@ -278,41 +278,57 @@ const pushResults = async (pageResults, allIssues) => {

currRuleFromAllIssues.totalItems += count;

if (!(url in currRuleFromAllIssues.pagesAffected)) {
currRuleFromAllIssues.pagesAffected[url] = {
pageTitle,
items: [],
...(filePath && { filePath }),
};
/*if (actualUrl) {
currRuleFromAllIssues.pagesAffected[url].actualUrl = actualUrl;
// Deduct duplication count from totalItems
currRuleFromAllIssues.totalItems -= 1;
// Previously using pagesAffected.length to display no. of pages affected
// However, since pagesAffected array contains duplicates, we need to deduct the duplicates
// Hence, start with negative offset, will add pagesAffected.length later
currRuleFromAllIssues.numberOfPagesAffectedAfterRedirects -= 1;
currCategoryFromAllIssues.totalItems -= 1;
}*/
if (isCustomFlow) {
const { pageIndex, pageImagePath } = pageResults;
currRuleFromAllIssues.pagesAffected[pageIndex] = {
url,
pageTitle,
pageImagePath,
items: [],
}
currRuleFromAllIssues.pagesAffected[pageIndex].items.push(...items);
} else {
if (!(url in currRuleFromAllIssues.pagesAffected)) {
currRuleFromAllIssues.pagesAffected[url] = {
pageTitle,
items: [],
...(filePath && { filePath }),
};
/*if (actualUrl) {
currRuleFromAllIssues.pagesAffected[url].actualUrl = actualUrl;
// Deduct duplication count from totalItems
currRuleFromAllIssues.totalItems -= 1;
// Previously using pagesAffected.length to display no. of pages affected
// However, since pagesAffected array contains duplicates, we need to deduct the duplicates
// Hence, start with negative offset, will add pagesAffected.length later
currRuleFromAllIssues.numberOfPagesAffectedAfterRedirects -= 1;
currCategoryFromAllIssues.totalItems -= 1;
}*/
}

currRuleFromAllIssues.pagesAffected[url].items.push(...items);
// currRuleFromAllIssues.numberOfPagesAffectedAfterRedirects +=
// currRuleFromAllIssues.pagesAffected.length;
}

currRuleFromAllIssues.pagesAffected[url].items.push(...items);
// currRuleFromAllIssues.numberOfPagesAffectedAfterRedirects +=
// currRuleFromAllIssues.pagesAffected.length;
});
});
};

const flattenAndSortResults = allIssues => {
const flattenAndSortResults = (allIssues, isCustomFlow) => {
['mustFix', 'goodToFix', 'passed'].forEach(category => {
allIssues.totalItems += allIssues.items[category].totalItems;
allIssues.items[category].rules = Object.entries(allIssues.items[category].rules)
.map(ruleEntry => {
const [rule, ruleInfo] = ruleEntry;
ruleInfo.pagesAffected = Object.entries(ruleInfo.pagesAffected)
.map(pageEntry => {
const [url, pageInfo] = pageEntry;
return { url, ...pageInfo };
if (isCustomFlow) {
const [pageIndex, pageInfo] = pageEntry;
return { pageIndex, ...pageInfo };
} else {
const [url, pageInfo] = pageEntry;
return { url, ...pageInfo };
}
})
.sort((page1, page2) => page2.items.length - page1.items.length);
return { rule, ...ruleInfo };
Expand Down Expand Up @@ -398,24 +414,24 @@ export const generateArtifacts = async (
goodToFix: { description: itemTypeDescription.goodToFix, totalItems: 0, rules: {} },
passed: { description: itemTypeDescription.passed, totalItems: 0, rules: {} },
},
proxy: constants.proxy
};
const allFiles = await extractFileNames(directory);
const isCustomFlow = scanType === 'Customized';

const jsonArray = await Promise.all(
allFiles.map(async file => parseContentToJson(`${directory}/${file}`)),
);

await Promise.all(
jsonArray.map(async pageResults => {
await pushResults(pageResults, allIssues);
await pushResults(pageResults, allIssues, isCustomFlow);
}),
).catch(flattenIssuesError => {
consoleLogger.info('An error has occurred when flattening the issues, please try again.');
silentLogger.error(flattenIssuesError.stack);
});

flattenAndSortResults(allIssues);
flattenAndSortResults(allIssues, isCustomFlow);

allIssues.totalPages = allIssues.totalPagesScanned + allIssues.totalPagesNotScanned;

Expand Down
Loading

0 comments on commit 4fc171b

Please sign in to comment.