UNPKG

accessibility-checker

Version:

An automated testing tools for accessibility testing using Puppeteer, Selenium, or Zombie

934 lines (931 loc) 53.1 kB
"use strict"; /****************************************************************************** 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. *****************************************************************************/ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ACReporterXLSX = void 0; const IConfig_js_1 = require("../config/IConfig.js"); const IGuideline_js_1 = require("../engine/IGuideline.js"); const IRule_js_1 = require("../engine/IRule.js"); const ReporterManager_js_1 = require("./ReporterManager.js"); const ExcelJS = __importStar(require("exceljs")); function dropDupes(arr) { let dupes = {}; return arr.filter(item => { if (item.toString() in dupes) { return false; } { return dupes[item.toString()] = true; } }); } class ACReporterXLSX { name() { return "xlsx"; } generateReport(_reportData) { } generateSummary(config, rulesets, endReport, summaryData) { return __awaiter(this, void 0, void 0, function* () { let storedReport = ReporterManager_js_1.ReporterManager.uncompressReport(summaryData[0]); let cfgRulesets = rulesets.filter(rs => config.policies.includes(rs.id)); let policyInfo = {}; for (const rs of cfgRulesets) { for (const cp of rs.checkpoints) { for (const rule of cp.rules) { policyInfo[rule.id] = policyInfo[rule.id] || { tkLevels: [], cps: [] }; policyInfo[rule.id].tkLevels.push(rule.toolkitLevel); policyInfo[rule.id].cps.push(cp); } } } for (const ruleId in policyInfo) { policyInfo[ruleId].tkLevels = dropDupes(policyInfo[ruleId].tkLevels); policyInfo[ruleId].tkLevels.sort(); } // const buffer: any = await workbook.xlsx.writeBuffer(); let startScan = new Date(storedReport.engineReport.summary.startScan); let reportFilename = `results_${startScan.toISOString().replace(/:/g, "-")}.xlsx`; if (config.outputFilenameTimestamp === false) { reportFilename = `results.xlsx`; } return { summaryPath: reportFilename, summary: (filename) => __awaiter(this, void 0, void 0, function* () { // @ts-ignore const workbook = new ExcelJS.stream.xlsx.WorkbookWriter({ filename, useStyles: true }); // const workbook = new ExcelJS.Workbook({ useStyles: true }); ACReporterXLSX.createOverviewSheet(config, summaryData, workbook); ACReporterXLSX.createScanSummarySheet(config, summaryData, workbook); ACReporterXLSX.createIssueSummarySheet(config, policyInfo, summaryData, workbook); ACReporterXLSX.createIssuesSheet(config, policyInfo, summaryData, workbook); ACReporterXLSX.createDefinitionsSheet(workbook); workbook.commit(); }) }; }); } static createOverviewSheet(config, compressedScans, workbook) { let violations = 0; let needsReviews = 0; let recommendations = 0; let archived = 0; let totalIssues = 0; let startScan = 0; // BIG QUESTION: is report // 1. for current scan (from menu) // 2. all stored scans (from menu) // 3. selected stored scans (from scan manager) for (const compressedScan of compressedScans) { let storedScan = ReporterManager_js_1.ReporterManager.uncompressReport(compressedScan); if (startScan === 0) startScan = storedScan.engineReport.summary.startScan; const counts = storedScan.engineReport.summary.counts; violations += counts.violation; needsReviews += counts.potentialviolation + counts.manual; recommendations += counts.recommendation + counts.potentialrecommendation; archived += counts.ignored; } totalIssues = violations + needsReviews + recommendations + archived; const worksheet = workbook.addWorksheet("Overview"); // Report Title worksheet.mergeCells('A1', "E1"); const titleRow = worksheet.getRow(1); titleRow.height = 27; const cellA1 = worksheet.getCell('A1'); cellA1.value = "Accessibility Scan Report"; cellA1.alignment = { vertical: "middle", horizontal: "left" }; cellA1.font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; cellA1.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; // what are column widths - can't get it till you set it const colWidthData = [ { col: 'A', width: 15.1 }, { col: 'B', width: 15.9 }, { col: 'C', width: 16.23 }, { col: 'D', width: 19.4 }, ]; for (let i = 0; i < 4; i++) { worksheet.getColumn(colWidthData[i].col).width = colWidthData[i].width; } // note except for Report Date this is the same for all scans const rowData = [ { key1: 'Tool:', key2: 'IBM Equal Access Accessibility Checker' }, // {key1: 'Version:', key2: "chrome.runtime.getManifest().version"}, { key1: 'Version:', key2: config.toolID }, //@ts-ignore // {key1: 'Rule set:', key2: (theCurrentScan.ruleSet === "Latest Deployment") ? archives[1].name : theCurrentScan.ruleSet }, { key1: 'Rule set:', key2: config.ruleArchiveLabel }, { key1: 'Guidelines:', key2: config.policies.join(", ") }, { key1: 'Report date:', key2: new Date(startScan).toLocaleString() }, // do we need to get actual date? { key1: 'Scans:', key2: "" + compressedScans.length }, // *** NEED TO FIX FOR selected // { key1: 'Pages:', key2: "" } ]; for (let idx = 0; idx < rowData.length; ++idx) { worksheet.mergeCells(`B${idx + 2}`, `E${idx + 2}`); let i = idx + 2; worksheet.getRow(i).height = 12; // results in a row height of 16 worksheet.getRow(i).getCell(1).font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; worksheet.getRow(i).getCell(2).font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; worksheet.getRow(i).getCell(1).alignment = { horizontal: "left" }; worksheet.getRow(i).getCell(2).alignment = { horizontal: "left" }; // if (i == 7) { // worksheet.getRow(i).getCell(1).alignment = { vertical: "top" }; // worksheet.getRow(i).getCell(2).alignment = { wrapText: true }; // } worksheet.getRow(i).getCell(1).value = rowData[i - 2].key1; worksheet.getRow(i).getCell(2).value = rowData[i - 2].key2; } // Summary Title worksheet.mergeCells('A11', "E11"); const summaryRow = worksheet.getRow(11); summaryRow.height = 27; const cellA11 = worksheet.getCell('A11'); cellA11.value = "Summary"; cellA11.alignment = { vertical: "middle", horizontal: "left" }; cellA11.font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; cellA11.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; // Scans info Headers worksheet.getRow(12).height = 16; // actual height is const cellA12 = worksheet.getCell('A12'); cellA12.value = "Total issues"; const cellB12 = worksheet.getCell('B12'); cellB12.value = "Violations"; const cellC12 = worksheet.getCell('C12'); cellC12.value = "Needs review"; const cellD12 = worksheet.getCell('D12'); cellD12.value = "Recommendations"; const cellE12 = worksheet.getCell('E12'); cellE12.value = "Archived"; const cellObjects1 = [cellA12, cellB12, cellC12, cellD12, cellE12]; for (let i = 0; i < cellObjects1.length; i++) { cellObjects1[i].alignment = { vertical: "middle", horizontal: "center" }; if (i == 1 || i == 2 || i == 3 || i === 4) { cellObjects1[i].font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; } else { cellObjects1[i].font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 12 }; } // cellObjects1[i].fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFC65911'} }; cellObjects1[i].border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; } cellA12.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF000000' } }; cellB12.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE4AAAF' } }; cellC12.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF4E08A' } }; cellD12.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF96A9D7' } }; // Scans info Values worksheet.getRow(13).height = 27; // actual height is const cellA13 = worksheet.getCell('A13'); cellA13.value = totalIssues; const cellB13 = worksheet.getCell('B13'); cellB13.value = violations; const cellC13 = worksheet.getCell('C13'); cellC13.value = needsReviews; const cellD13 = worksheet.getCell('D13'); cellD13.value = recommendations; const cellE13 = worksheet.getCell('E13'); cellE13.value = archived; const cellObjects2 = [cellA13, cellB13, cellC13, cellD13, cellE13]; for (let i = 0; i < cellObjects2.length; i++) { cellObjects2[i].alignment = { vertical: "middle", horizontal: "center" }; cellObjects2[i].font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; // cellObjects2[i].fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; cellObjects2[i].border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; } } static createScanSummarySheet(config, compressedScans, workbook) { const worksheet = workbook.addWorksheet("Scan summary"); // Scans info Headers worksheet.getRow(1).height = 39; // actual height is 52 const colWidthData = [ { col: 'A', width: 27.0 }, { col: 'B', width: 46.0 }, { col: 'C', width: 20.17 }, { col: 'D', width: 18.5 }, { col: 'E', width: 17.17 }, { col: 'F', width: 17.17 }, { col: 'G', width: 17.17 }, { col: 'H', width: 17.17 }, { col: 'I', width: 17.17 }, ]; for (let i = 0; i < 9; i++) { worksheet.getColumn(colWidthData[i].col).width = colWidthData[i].width; } const cellA1 = worksheet.getCell('A1'); cellA1.value = "Page title"; const cellB1 = worksheet.getCell('B1'); cellB1.value = "Page url"; const cellC1 = worksheet.getCell('C1'); cellC1.value = "Scan label"; const cellObjects1 = [cellA1, cellB1, cellC1]; for (let i = 0; i < cellObjects1.length; i++) { cellObjects1[i].alignment = { vertical: "middle", horizontal: "left" }; cellObjects1[i].font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 12 }; cellObjects1[i].fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; cellObjects1[i].border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; } const cellD1 = worksheet.getCell('D1'); cellD1.value = "Violations"; const cellE1 = worksheet.getCell('E1'); cellE1.value = "Needs review"; const cellF1 = worksheet.getCell('F1'); cellF1.value = "Recommendations"; const cellG1 = worksheet.getCell('G1'); cellG1.value = "Archived"; const cellH1 = worksheet.getCell('H1'); cellH1.value = "% elements without violations"; const cellI1 = worksheet.getCell('I1'); cellI1.value = "% elements without violations or items to review"; const cellObjects2 = [cellD1, cellE1, cellF1, cellG1, cellH1, cellI1]; for (let i = 0; i < cellObjects2.length; i++) { cellObjects2[i].alignment = { vertical: "middle", horizontal: "center", wrapText: true }; if (i == 0 || i == 1 || i == 2 || i == 3) { cellObjects2[i].font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; } else { cellObjects2[i].font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 12 }; } // cellObjects2[i].fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFC65911'} }; cellObjects2[i].border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; } cellD1.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE4AAAF' } }; cellE1.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF4E08A' } }; cellF1.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF96A9D7' } }; cellG1.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFFFFFFF' } }; cellH1.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF000000' } }; cellI1.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF000000' } }; for (const compressedScan of compressedScans) { let storedScan = ReporterManager_js_1.ReporterManager.uncompressReport(compressedScan); let counts = storedScan.engineReport.summary.counts; let row = worksheet.addRow([ storedScan.pageTitle, storedScan.engineReport.summary.URL, storedScan.label, counts.violation, counts.potentialviolation + counts.manual, counts.recommendation + counts.potentialrecommendation, counts.ignored, (100 * (counts.elements - counts.elementsViolation) / counts.elements).toFixed(0), (100 * (counts.elements - counts.elementsViolationReview) / counts.elements).toFixed(0), ]); row.height = 37; // actual height is for (let i = 1; i < 4; i++) { row.getCell(i).alignment = { vertical: "middle", horizontal: "left", wrapText: true }; row.getCell(i).font = { name: "Calibri", color: { argb: "00000000" }, size: 12 }; } for (let i = 4; i < 10; i++) { row.getCell(i).alignment = { vertical: "middle", horizontal: "center", wrapText: true }; row.getCell(i).font = { name: "Calibri", color: { argb: "00000000" }, size: 12 }; // row.getCell(i).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; row.getCell(i).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; } row.commit(); } worksheet.commit(); } static buildIssueSummaryLevel(worksheet, fillColor, title, levelCount, levelrowValues) { // Level 1 Violation title const level1ViolationRow = worksheet.addRow(["", 0]); level1ViolationRow.height = 18; // target is 21 const cellA6 = level1ViolationRow.getCell(1); cellA6.value = ` ${title}`; cellA6.alignment = { vertical: "middle", horizontal: "left" }; cellA6.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; cellA6.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: fillColor } }; level1ViolationRow.getCell(1).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, // right: {style:'thin', color: {argb: 'FFA6A6A6'}} }; const cellB6 = level1ViolationRow.getCell(2); cellB6.value = levelCount; // total level violations cellB6.alignment = { vertical: "middle", horizontal: "right" }; cellB6.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; cellB6.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: fillColor } }; level1ViolationRow.getCell(2).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; // Level 1 Violation Rows // build rows let rowArray = []; // let row:any =[]; for (const property in levelrowValues) { let row = [" " + `${property}`, parseInt(`${levelrowValues[property]}`) ]; rowArray.push(row); } // sort array according to count rowArray.sort((a, b) => (a[1] < b[1]) ? 1 : -1); // add array of rows for (const rowInfo of rowArray) { const row = worksheet.addRow(rowInfo); row.height = 14; row.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; row.getCell(2).alignment = { vertical: "middle", horizontal: "right" }; row.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; // row.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; // row.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFf8cbad'} }; row.getCell(1).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, // right: {style:'thin', color: {argb: 'FFA6A6A6'}} }; row.getCell(2).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, // left: {style:'thin', color: {argb: 'FFA6A6A6'}}, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; row.commit(); } } static buildIssueSummaryTKLevel(worksheet, title, levelCounts, levelVrowValues, levelNRrowValues, levelRrowValues, levelArowValues) { ///////////////////////////// // build Level title ///////////////////////////// const level1Row = worksheet.addRow(["", 0]); level1Row.height = 27; // actual is 36 const cellA5 = level1Row.getCell(1); cellA5.value = title; cellA5.alignment = { vertical: "middle", horizontal: "left" }; cellA5.font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; cellA5.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; const cellB5 = level1Row.getCell(2); cellB5.value = levelCounts[0]; // total Level 1 issues cellB5.alignment = { vertical: "middle", horizontal: "right" }; cellB5.font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; cellB5.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; ACReporterXLSX.buildIssueSummaryLevel(worksheet, "FFE4AAAF", "Violation", levelCounts[1], levelVrowValues); ACReporterXLSX.buildIssueSummaryLevel(worksheet, "FFF4E08A", "Needs review", levelCounts[2], levelNRrowValues); ACReporterXLSX.buildIssueSummaryLevel(worksheet, "FF96A9D7", "Recommendation", levelCounts[3], levelRrowValues); if (levelCounts[4] > 0) { ACReporterXLSX.buildIssueSummaryLevel(worksheet, "FFCCCCCC", "Archived", levelCounts[4], levelArowValues); } } static createIssueSummarySheet(config, policyInfo, compressedScans, workbook) { let violations = 0; let needsReviews = 0; let recommendations = 0; let archive = 0; let totalIssues = 0; for (let i = 0; i < compressedScans.length; i++) { let storedScan = ReporterManager_js_1.ReporterManager.uncompressReport(compressedScans[i]); let counts = storedScan.engineReport.summary.counts; violations += counts.violation; needsReviews += counts.potentialviolation + counts.manual; recommendations += counts.recommendation + counts.potentialrecommendation; archive += counts.ignored; } totalIssues = violations + needsReviews + recommendations; // counts let level1Counts = [0, 0, 0, 0, 0]; // level 1 total issues, violations, needs reviews, recommendations let level2Counts = [0, 0, 0, 0, 0]; let level3Counts = [0, 0, 0, 0, 0]; let level4Counts = [0, 0, 0, 0, 0]; let level1V = []; let level2V = []; let level3V = []; let level4V = []; let level1NR = []; let level2NR = []; let level3NR = []; let level4NR = []; let level1R = []; let level2R = []; let level3R = []; let level4R = []; let level1A = []; let level2A = []; let level3A = []; let level4A = []; for (const compressedScan of compressedScans) { let scan = ReporterManager_js_1.ReporterManager.uncompressReport(compressedScan); for (const issue of scan.engineReport.results) { if (!(issue.ruleId in policyInfo)) { policyInfo[issue.ruleId] = { tkLevels: [], cps: [] }; } let levelCounts, levelV, levelNR, levelR, levelA; const issuePolicyInfo = policyInfo[issue.ruleId]; if (issuePolicyInfo.tkLevels.includes(IGuideline_js_1.eToolkitLevel.LEVEL_ONE)) { levelCounts = level1Counts; levelV = level1V; levelNR = level1NR; levelR = level1R; levelA = level1A; } else if (issuePolicyInfo.tkLevels.includes(IGuideline_js_1.eToolkitLevel.LEVEL_TWO)) { levelCounts = level2Counts; levelV = level2V; levelNR = level2NR; levelR = level2R; levelA = level2A; } else if (issuePolicyInfo.tkLevels.includes(IGuideline_js_1.eToolkitLevel.LEVEL_THREE)) { levelCounts = level3Counts; levelV = level3V; levelNR = level3NR; levelR = level3R; levelA = level3A; } else if (issuePolicyInfo.tkLevels.includes(IGuideline_js_1.eToolkitLevel.LEVEL_FOUR)) { levelCounts = level4Counts; levelV = level4V; levelNR = level4NR; levelR = level4R; levelA = level4A; } if (issue.value[1] !== IRule_js_1.eRuleConfidence.PASS) { ++levelCounts[0]; } if (issue.ignored) { ++levelCounts[4]; levelA.push(issue.message.substring(0, 32767)); } else if (issue.level === IConfig_js_1.eRuleLevel.violation) { ++levelCounts[1]; levelV.push(issue.message.substring(0, 32767)); } else if (issue.level === IConfig_js_1.eRuleLevel.potentialviolation || issue.level === IConfig_js_1.eRuleLevel.manual) { ++levelCounts[2]; levelNR.push(issue.message.substring(0, 32767)); } else if (issue.level === IConfig_js_1.eRuleLevel.recommendation || issue.level === IConfig_js_1.eRuleLevel.potentialrecommendation) { ++levelCounts[3]; levelR.push(issue.message.substring(0, 32767)); } } } // @ts-ignore let level1VrowValues = this.countDuplicatesInArray(level1V); // note this returns an object // @ts-ignore let level1NRrowValues = this.countDuplicatesInArray(level1NR); // @ts-ignore let level1RrowValues = this.countDuplicatesInArray(level1R); // @ts-ignore let level1ArowValues = this.countDuplicatesInArray(level1A); // @ts-ignore let level2VrowValues = this.countDuplicatesInArray(level2V); // note this returns an object // @ts-ignore let level2NRrowValues = this.countDuplicatesInArray(level2NR); // @ts-ignore let level2RrowValues = this.countDuplicatesInArray(level2R); // @ts-ignore let level2ArowValues = this.countDuplicatesInArray(level2A); // @ts-ignore let level3VrowValues = this.countDuplicatesInArray(level3V); // note this returns an object // @ts-ignore let level3NRrowValues = this.countDuplicatesInArray(level3NR); // @ts-ignore let level3RrowValues = this.countDuplicatesInArray(level3R); // @ts-ignore let level3ArowValues = this.countDuplicatesInArray(level3A); // @ts-ignore let level4VrowValues = this.countDuplicatesInArray(level4V); // note this returns an object // @ts-ignore let level4NRrowValues = this.countDuplicatesInArray(level4NR); // @ts-ignore let level4RrowValues = this.countDuplicatesInArray(level4R); // @ts-ignore let level4ArowValues = this.countDuplicatesInArray(level4A); const worksheet = workbook.addWorksheet("Issue summary"); // Approach: // 1. sort by levels // 2. for each level sort by V, NR and R // 3. for each V, NR, and R in a level get issue dup counts // 4. build the rows // build Issue summary title worksheet.mergeCells('A1', "B1"); const titleRow = worksheet.getRow(1); titleRow.height = 27; // actual is 36 const cellA1 = worksheet.getCell('A1'); cellA1.value = "Issue summary"; cellA1.alignment = { vertical: "middle", horizontal: "left" }; cellA1.font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; cellA1.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; const colWidthData = [ { col: 'A', width: 155.51 }, // note .84 added to actual width { col: 'B', width: 21.16 }, ]; for (let i = 0; i < 2; i++) { worksheet.getColumn(colWidthData[i].col).width = colWidthData[i].width; } // build Description title worksheet.mergeCells('A2', "B2"); const descriptionRow = worksheet.getRow(2); descriptionRow.height = 20.25; // actual is 27 const cellA2 = worksheet.getCell("A2"); cellA2.value = " In the IBM Equal Access Toolkit, issues are divided into three levels (1-3). Tackle the levels in order to address some of the most impactful issues first."; cellA2.alignment = { vertical: "middle", horizontal: "left" }; cellA2.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; // cellA2.fill = { type: 'pattern', pattern: 'solid', fgColor:{argb:'FFCCC0DA'} }; // build Total issues found: title // worksheet.mergeCells('A3', "B3"); const totalIssuesRow = worksheet.getRow(3); totalIssuesRow.height = 27; // actual is 36 const cellA3 = worksheet.getCell("A3"); cellA3.value = "Total issues found:"; cellA3.alignment = { vertical: "middle", horizontal: "left" }; cellA3.font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; cellA3.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF000000' } }; const cellB3 = worksheet.getCell("B3"); cellB3.value = totalIssues; cellB3.alignment = { vertical: "middle", horizontal: "right" }; cellB3.font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; cellB3.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF000000' } }; // build Number of issues title const numberOfIssuesRow = worksheet.getRow(4); numberOfIssuesRow.height = 20.25; // actual is 27 const cellA4 = worksheet.getCell("A4"); // no value cellA4.alignment = { vertical: "middle", horizontal: "left" }; cellA4.border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFFFFFFF' } } }; const cellB4 = worksheet.getCell("B4"); cellB4.value = "Number of issues"; cellB4.alignment = { vertical: "middle", horizontal: "right" }; cellB4.font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; cellB4.border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFFFFFFF' } }, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; ACReporterXLSX.buildIssueSummaryTKLevel(worksheet, "Level 1 - the most essential issues to address", level1Counts, level1VrowValues, level1NRrowValues, level1RrowValues, level1ArowValues); ACReporterXLSX.buildIssueSummaryTKLevel(worksheet, "Level 2 - the next most important issues", level2Counts, level2VrowValues, level2NRrowValues, level2RrowValues, level2ArowValues); ACReporterXLSX.buildIssueSummaryTKLevel(worksheet, "Level 3 - necessary to meet requirements", level3Counts, level3VrowValues, level3NRrowValues, level3RrowValues, level3ArowValues); ACReporterXLSX.buildIssueSummaryTKLevel(worksheet, "Level 4 - further recommended improvements to accessibility", level4Counts, level4VrowValues, level4NRrowValues, level4RrowValues, level4ArowValues); worksheet.commit(); } static createIssuesSheet(config, policyInfo, compressedScans, workbook) { const valueMap = { "VIOLATION": { "POTENTIAL": "Needs review", "FAIL": "Violation", "PASS": "Pass", "MANUAL": "Needs review" }, "RECOMMENDATION": { "POTENTIAL": "Recommendation", "FAIL": "Recommendation", "PASS": "Pass", "MANUAL": "Recommendation" }, "INFORMATION": { "POTENTIAL": "Needs review", "FAIL": "Violation", "PASS": "Pass", "MANUAL": "Recommendation" } }; const worksheet = workbook.addWorksheet("Issues"); // build rows let rowArray = []; for (const compressedScan of compressedScans) { let storedScan = ReporterManager_js_1.ReporterManager.uncompressReport(compressedScan); for (const item of storedScan.engineReport.results) { if (!(item.ruleId in policyInfo)) { policyInfo[item.ruleId] = { tkLevels: [], cps: [] }; } let polInfo = policyInfo[item.ruleId]; let cps = polInfo.cps.filter(cp => { let ruleInfo = cp.rules.find(ruleInfo => ruleInfo.id === item.ruleId && (!ruleInfo.reasonCodes || ruleInfo.reasonCodes.includes("" + item.reasonId))); return !!ruleInfo; }); let wcagLevels = dropDupes(cps.map(cp => cp.wcagLevel)); wcagLevels.sort(); let cpStrs = dropDupes(cps.map(cp => `${cp.num} ${cp.name}`)); cpStrs.sort(); let row = [ storedScan.pageTitle, storedScan.engineReport.summary.URL, storedScan.label, this.stringHash(item.ruleId + item.path.dom), `${valueMap[item.value[0]][item.value[1]]}${item.ignored ? ` (Archived)` : ``}`, polInfo.tkLevels.join(", "), cpStrs.join("; "), wcagLevels.join(", "), item.ruleId, item.message.substring(0, 32767), //max ength for MS Excel 32767 characters this.get_element(item.snippet), item.snippet.substring(0, 32767), item.path.dom, item.help, item.source // engine_end_point + '/tools/help/' + item.ruleId ]; // let row = [myStoredData[i][0], myStoredData[i][1], storedScans[j].label, // myStoredData[i][3], myStoredData[i][4], Number.isNaN(myStoredData[i][5]) ? "n/a" : myStoredData[i][5], // myStoredData[i][6], Number.isNaN(myStoredData[i][5]) ? "n/a" : myStoredData[i][7], myStoredData[i][8], // myStoredData[i][9], myStoredData[i][10], myStoredData[i][11], // myStoredData[i][12], myStoredData[i][13] // ]; rowArray.push(row); } } // column widths const colWidthData = [ { col: 'A', width: 18.0, alignment: { vertical: "middle", horizontal: "left" } }, { col: 'B', width: 20.5, alignment: { vertical: "middle", horizontal: "left" } }, { col: 'C', width: 21.0, alignment: { vertical: "middle", horizontal: "center" } }, { col: 'D', width: 18.5, alignment: { vertical: "middle", horizontal: "left" } }, { col: 'E', width: 17.0, alignment: { vertical: "middle", horizontal: "center" } }, { col: 'F', width: 17.17, alignment: { vertical: "middle", horizontal: "center" } }, { col: 'G', width: 17.17, alignment: { vertical: "middle", horizontal: "left" } }, { col: 'H', width: 17.17, alignment: { vertical: "middle", horizontal: "center" } }, { col: 'I', width: 17.17, alignment: { vertical: "middle", horizontal: "left" } }, { col: 'J', width: 17.17, alignment: { vertical: "middle", horizontal: "left" } }, { col: 'K', width: 14.00, alignment: { vertical: "middle", horizontal: "center" } }, { col: 'L', width: 17.17, alignment: { vertical: "middle", horizontal: "left" } }, { col: 'M', width: 43.00, alignment: { vertical: "middle", horizontal: "left" } }, { col: 'N', width: 17.17, alignment: { vertical: "middle", horizontal: "fill" } }, ]; for (let i = 0; i < 14; i++) { worksheet.getColumn(colWidthData[i].col).width = colWidthData[i].width; worksheet.getColumn(colWidthData[i].col).alignment = colWidthData[i].alignment; } // add table to a sheet let headRow = worksheet.addRow([ "Page title", "Page URL", "Scan label", "Issue ID", "Issue type", "Toolkit level", "Checkpoint", "WCAG level", "Rule", "Issue", "Element", "Code", "Xpath", "Help", "" ]); // set font and alignment for the header cells for (let i = 1; i < 15; i++) { headRow.getCell(i).alignment = { vertical: "middle", horizontal: "center", wrapText: true }; headRow.getCell(i).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 12 }; headRow.getCell(i).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; headRow.getCell(i).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; } // height for header row headRow.height = 24; headRow.commit(); for (const rowInfo of rowArray) { const row = worksheet.addRow(rowInfo); row.height = 14; for (let j = 1; j <= 14; j++) { row.getCell(j).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; } row.commit(); } for (const key in worksheet) { if (typeof worksheet[key] === "function") { console.log(key); } } // worksheet.addTable({ // name: 'MyTable', // ref: 'A1', // headerRow: true, // // totalsRow: true, // style: { // theme: 'TableStyleMedium2', // showRowStripes: true, // }, // columns: [ // { name: 'Page title', filterButton: true }, // { name: 'Page URL', filterButton: true }, // { name: 'Scan label', filterButton: true }, // { name: 'Issue ID', filterButton: true }, // { name: 'Issue type', filterButton: true }, // { name: 'Toolkit level', filterButton: true }, // { name: 'Checkpoint', filterButton: true }, // { name: 'WCAG level', filterButton: true }, // { name: 'Rule', filterButton: true }, // { name: 'Issue', filterButton: true }, // { name: 'Element', filterButton: true }, // { name: 'Code', filterButton: true }, // { name: 'Xpath', filterButton: true }, // { name: 'Help', filterButton: true }, // ], // rows: rowArray // }); // for (let i = 2; i <= rowArray.length + 1; i++) { // worksheet.getRow(i).height = 14; // for (let j = 1; j <= 14; j++) { // worksheet.getRow(i).getCell(j).border = { // top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, // left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, // bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, // right: { style: 'thin', color: { argb: 'FFA6A6A6' } } // } // } // worksheet.getRow(i).commit(); // } worksheet.commit(); } static createDefinitionsSheet(workbook) { const worksheet = workbook.addWorksheet("Definition of fields"); // "Definition of fields" title worksheet.mergeCells('A1', "B1"); const titleRow = worksheet.getRow(1); titleRow.height = 36; // actual is 48 titleRow.getCell(1).value = "Definition of fields"; titleRow.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; titleRow.getCell(1).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: "20" }; titleRow.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; const colWidthData = [ { col: 'A', width: '41.51' }, // note .84 added to actual width { col: 'B', width: '119.51' }, ]; for (let i = 0; i < 2; i++) { worksheet.getColumn(colWidthData[i].col).width = colWidthData[i].width; } // blank row worksheet.mergeCells('A2', "B2"); const blankRow = worksheet.getRow(2); blankRow.height = 12; // actual is 16 // "Scan summary and Issue summary" title worksheet.mergeCells('A3', "B3"); const summaryRow = worksheet.getRow(3); summaryRow.height = 20; // actual is 26.75 summaryRow.getCell(1).value = "Scan summary and Issue summary"; summaryRow.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; summaryRow.getCell(1).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; summaryRow.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; // row 4 Field / Definition const row4 = worksheet.getRow(4); row4.height = 16; // actual is row4.getCell(1).value = "Field"; row4.getCell(2).value = "Definition"; row4.getCell(1).alignment = row4.getCell(2).alignment = { vertical: "middle", horizontal: "left" }; row4.getCell(1).font = row4.getCell(2).font = { name: "Calibri", color: { argb: "FF000000" }, size: 16 }; row4.getCell(1).fill = row4.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFCCC0DA' } }; row4.getCell(1).border = row4.getCell(2).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; // rows 5-13 // set row height for rows 5-13 for (let i = 5; i < 14; i++) { worksheet.getRow(i).height = 12; // results in a row height of 16 } let rowData = [ { key1: 'Page', key2: 'Identifies the page or html file that was scanned.' }, { key1: 'Scan label', key2: 'Label for the scan. Default values can be edited in the Accessibility Checker before saving this report, or programmatically assigned in automated testing.' }, { key1: 'Violations', key2: 'Accessibility failures that need to be corrected.' }, { key1: 'Needs review', key2: 'Issues that may not be a violation. These need a manual review to identify whether there is an accessibility problem.' }, { key1: 'Recommendations', key2: 'Opportunities to apply best practices to further improve accessibility.' }, { key1: '% elements without violations', key2: 'Percentage of elements on the page that had no violations found.' }, { key1: '% elements without violations or items to review', key2: 'Percentage of elements on the page that had no violations found and no items to review.' }, { key1: 'Level 1,2,3', key2: 'Priority level defined by the IBM Equal Access Toolkit. See https://www.ibm.com/able/toolkit/plan/overview#pace-of-completion for details.' } ]; for (let i = 5; i < rowData.length + 5; i++) { worksheet.getRow(i).getCell(1).font = worksheet.getRow(i).getCell(2).font = { name: "Calibri", color: { argb: "FF000000" }, size: 12 }; worksheet.getRow(i).getCell(1).alignment = worksheet.getRow(i).getCell(2).alignment = { horizontal: "left" }; } for (let i = 5; i < rowData.length + 5; i++) { worksheet.getRow(i).getCell(1).value = rowData[i - 5].key1; worksheet.getRow(i).getCell(2).value = rowData[i - 5].key2; } // "Scan summary and Issue summary" title worksheet.mergeCells('A14', "B14"); const issuesRow = worksheet.getRow(14); issuesRow.height = 20; // actual is 26.75 issuesRow.getCell(1).value = "Issues"; issuesRow.getCell(1).alignment = { vertical: "middle", horizontal: "left" }; issuesRow.getCell(1).font = { name: "Calibri", color: { argb: "FFFFFFFF" }, size: 16 }; issuesRow.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF403151' } }; // row 15 Field / Definition const row15 = worksheet.getRow(15); row15.height = 16; // actual is row15.getCell(1).value = "Field"; row15.getCell(2).value = "Definition"; row15.getCell(1).alignment = row15.getCell(2).alignment = { vertical: "middle", horizontal: "left" }; row15.getCell(1).font = row15.getCell(2).font = { name: "Calibri", color: { argb: "FF000000" }, size: 16 }; row15.getCell(1).fill = row15.getCell(2).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFCCC0DA' } }; row15.getCell(1).border = row15.getCell(2).border = { top: { style: 'thin', color: { argb: 'FFA6A6A6' } }, left: { style: 'thin', color: { argb: 'FFA6A6A6' } }, bottom: { style: 'thin', color: { argb: 'FFA6A6A6' } }, right: { style: 'thin', color: { argb: 'FFA6A6A6' } } }; // rows 16-28 // set row height for rows 16-28 for (let i = 16; i < 29; i++) { worksheet.getRow(i).height = 12; // results in a row height of 16 } rowData = []; rowData = [ { key1: 'Page', key2: 'Identifies the page or html file that was scanned.' }, { key1: 'Scan label', key2: 'Label for the scan. Default values can be edited in the Accessibility Checker before saving this report, or programmatically assigned in automated testing.' }, { key1: 'Issue ID', key2: 'Identifier for this issue within this page. Rescanning the same page will produce the same issue ID. ' }, { key1: 'Issue type', key2: 'Violation, needs review, or recommendation' }, { key1: 'Toolkit level'