From 586bf0ec82186fc77fe6bb6dd9daa30c02501b18 Mon Sep 17 00:00:00 2001 From: Tom Brunet Date: Fri, 6 Oct 2023 15:37:12 -0500 Subject: [PATCH 1/2] Add better guideline controls to engine --- .../src/v4/api/IGuideline.ts | 3 +- .../src/v4/checker/Checker.ts | 131 +++++++++++++++--- accessibility-checker-engine/tsconfig.json | 1 + common/module/src/engine/IGuideline.ts | 3 +- report-react/src/IReport.tsx | 3 +- 5 files changed, 116 insertions(+), 25 deletions(-) diff --git a/accessibility-checker-engine/src/v4/api/IGuideline.ts b/accessibility-checker-engine/src/v4/api/IGuideline.ts index 9fe96ee7d..a94e8d7f0 100644 --- a/accessibility-checker-engine/src/v4/api/IGuideline.ts +++ b/accessibility-checker-engine/src/v4/api/IGuideline.ts @@ -48,7 +48,8 @@ export type Checkpoint = { // or all if not specified reasonCodes?: string[], level: eRulePolicy, - toolkitLevel: eToolkitLevel + toolkitLevel: eToolkitLevel, + enabled?: boolean }> } diff --git a/accessibility-checker-engine/src/v4/checker/Checker.ts b/accessibility-checker-engine/src/v4/checker/Checker.ts index 1911d814d..1604c24f6 100644 --- a/accessibility-checker-engine/src/v4/checker/Checker.ts +++ b/accessibility-checker-engine/src/v4/checker/Checker.ts @@ -14,13 +14,13 @@ limitations under the License. *****************************************************************************/ -import { Issue, Rule as RuleV4, eRulePolicy } from "../api/IRule"; +import { Rule as RuleV4, eRulePolicy } from "../api/IRule"; import { Engine } from "../../v2/common/Engine"; import { ARIAMapper } from "../../v2/aria/ARIAMapper"; import { StyleMapper } from "../../v2/style/StyleMapper"; import { a11yRulesets } from "../rulesets"; import * as checkRulesV4 from "../rules"; -import { Checkpoint, Guideline, eGuidelineCategory } from "../api/IGuideline"; +import { Guideline, eGuidelineCategory } from "../api/IGuideline"; import { IEngine } from "../api/IEngine"; import { Report } from "../api/IReport"; import { IChecker } from "../api/IChecker"; @@ -84,14 +84,22 @@ _initialize(); export type Ruleset = Guideline; export class Checker implements IChecker { + private guidelines: Guideline[] = []; + engine: IEngine; - rulesets: Guideline[] = []; + /** + * @deprecated Use getGuidelines(). + */ + rulesets: Guideline[] = this.guidelines; + /** + * @deprecated Use getGuidelineIds(). + */ rulesetIds: string[] = []; rulesetRules: { [rsId: string]: string[] } = {}; ruleLevels : { [ruleId: string]: { [rsId: string] : eRulePolicy }} = {}; ruleCategory : { [ruleId: string]: { [rsId: string] : eGuidelineCategory }} = {}; - constructor() { + public constructor() { let engine = this.engine = new Engine(); engine.addMapper(new ARIAMapper()); @@ -106,29 +114,102 @@ export class Checker implements IChecker { } } + /** + * Adds a guideline to the engine. If the id already exists, the previous guideline will be replaced. + * @param guideline + */ addGuideline(guideline: Guideline) { - this.rulesets.push(guideline); + if (guideline.id in this.rulesetRules) { + this.removeGuideline(guideline.id); + } + this.guidelines.push(guideline); this.rulesetIds.push(guideline.id); const ruleIds = []; for (const cp of guideline.checkpoints) { cp.rules = cp.rules || []; for (const rule of cp.rules) { - ruleIds.push(rule.id); - this.ruleLevels[rule.id] = this.ruleLevels[rule.id] || {}; - this.ruleLevels[rule.id][guideline.id] = rule.level; - this.ruleCategory[rule.id] = this.ruleCategory[rule.id] || {}; - this.ruleCategory[rule.id][guideline.id] = guideline.category; + if (rule.enabled !== false) { + ruleIds.push(rule.id); + this.ruleLevels[rule.id] = this.ruleLevels[rule.id] || {}; + this.ruleLevels[rule.id][guideline.id] = rule.level; + this.ruleCategory[rule.id] = this.ruleCategory[rule.id] || {}; + this.ruleCategory[rule.id][guideline.id] = guideline.category; + } } } this.rulesetRules[guideline.id] = ruleIds; } - getGuidelines() { - return this.rulesets; + /** + * Enable a rule for all guidelines + * @param ruleId + */ + enableRule(ruleId: string) { + for (const guideline of this.getGuidelines()) { + let updated = false; + for (const cp of guideline.checkpoints) { + for (const rule of cp.rules) { + if (rule.enabled === false) { + updated = true; + delete rule.enabled; + } + } + } + if (updated) { + this.addGuideline(guideline); + } + } } - getGuidelineIds() { - return this.rulesetIds; + /** + * Disable a rule for all guidelines + * @param ruleId + */ + disableRule(ruleId: string) { + for (const guideline of this.getGuidelines()) { + let updated = false; + for (const cp of guideline.checkpoints) { + for (const rule of cp.rules) { + if (rule.enabled !== false) { + updated = true; + rule.enabled = false; + } + } + } + if (updated) { + this.addGuideline(guideline); + } + } + } + + /** + * Remove a guideline from the engine + * + * Generally, there isn't a good reason to do this. Users should just not select the guideline as an option in check + * @param guidelineId + */ + private removeGuideline(guidelineId: string) { + if (guidelineId in this.rulesetRules) { + delete this.rulesetRules[guidelineId]; + this.rulesets = this.guidelines = this.guidelines.filter(guideline => guideline.id !== guidelineId); + this.rulesetIds = this.getGuidelineIds(); + } + } + + /** + * Get the guidelines available in the engine + * @returns + */ + getGuidelines() : Guideline[] { + return JSON.parse(JSON.stringify(this.guidelines)); + } + + /** + * Get the ids of the guidelines available in the engine + * @returns + */ + getGuidelineIds() : string[] { + return this.guidelines.map(guideline => guideline.id); } /** @@ -139,19 +220,25 @@ export class Checker implements IChecker { this.addGuideline(rs); } - check(node: Node | Document, rsIds?: string | string[]) : Promise { + /** + * Perform a check of the specified node/document + * @param node DOMNode or Document on which to run the check + * @param guidelineIds Guideline ids to check with to specify which rules to run + * @returns + */ + check(node: Node | Document, guidelineIds?: string | string[]) : Promise { // Determine which rules to run let ruleIds : string[] = []; // Fix the input - if (!rsIds) { + if (!guidelineIds) { ruleIds = this.engine.getRulesIds(); } else{ - if (typeof rsIds === "string") { - rsIds = [rsIds]; + if (typeof guidelineIds === "string") { + guidelineIds = [guidelineIds]; } - for (const rsId of rsIds) { + for (const rsId of guidelineIds) { if (rsId in this.rulesetRules) { ruleIds = ruleIds.concat(this.rulesetRules[rsId]); } @@ -175,8 +262,8 @@ export class Checker implements IChecker { report.nls[result.ruleId][result.reasonId] = checkNls[result.ruleId][result.reasonId]; } } - result.value[0] = myThis.getLevel(rsIds as string[], result.ruleId); - result.category = myThis.getCategory(rsIds as string[], result.ruleId); + result.value[0] = myThis.getLevel(guidelineIds as string[], result.ruleId); + result.category = myThis.getCategory(guidelineIds as string[], result.ruleId); delete result.path.css; } return report; @@ -217,7 +304,7 @@ export class Checker implements IChecker { return eGuidelineCategory.OTHER; } if (!rsIds) { - rsIds = this.rulesetIds; + rsIds = this.getGuidelineIds(); } for (const rsId of rsIds) { if (rsId in rsInfo) { diff --git a/accessibility-checker-engine/tsconfig.json b/accessibility-checker-engine/tsconfig.json index 22a2364c6..4f338ec46 100644 --- a/accessibility-checker-engine/tsconfig.json +++ b/accessibility-checker-engine/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "declaration": true, "allowJs": false, "target": "ES5", "removeComments": false, diff --git a/common/module/src/engine/IGuideline.ts b/common/module/src/engine/IGuideline.ts index 9fe96ee7d..a94e8d7f0 100644 --- a/common/module/src/engine/IGuideline.ts +++ b/common/module/src/engine/IGuideline.ts @@ -48,7 +48,8 @@ export type Checkpoint = { // or all if not specified reasonCodes?: string[], level: eRulePolicy, - toolkitLevel: eToolkitLevel + toolkitLevel: eToolkitLevel, + enabled?: boolean }> } diff --git a/report-react/src/IReport.tsx b/report-react/src/IReport.tsx index 6e234f2aa..f59960676 100644 --- a/report-react/src/IReport.tsx +++ b/report-react/src/IReport.tsx @@ -58,7 +58,8 @@ export interface ICheckpoint { // or all if not specified reasonCodes?: string[], level: string, - toolkitLevel: string + toolkitLevel: string, + enabled?: boolean }> } From a8076e3803e39631d58f52181f5c77e0a693178c Mon Sep 17 00:00:00 2001 From: Tom Brunet Date: Fri, 6 Oct 2023 15:43:48 -0500 Subject: [PATCH 2/2] Copy the type definitions for the various engine imports --- accessibility-checker-engine/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/accessibility-checker-engine/package.json b/accessibility-checker-engine/package.json index aa2cabef8..4682c5d4a 100644 --- a/accessibility-checker-engine/package.json +++ b/accessibility-checker-engine/package.json @@ -4,10 +4,10 @@ "description": "IBM Equal Access Accessibility Checker Engine", "private": false, "scripts": { - "build-node": "npm run prebuild && npx webpack --config webpack-node.config.js", - "build-node-debug": "npm run prebuild && npx webpack --config webpack-node-debug.config.js", - "build": "npm run prebuild && npx webpack --config webpack.config.js", - "build-debug": "npm run prebuild && npx webpack --config webpack-debug.config.js", + "build-node": "npm run prebuild && npx webpack --config webpack-node.config.js && shx cp dist/index.d.ts dist/ace-node.d.ts", + "build-node-debug": "npm run prebuild && npx webpack --config webpack-node-debug.config.js && shx cp dist/index.d.ts dist/ace-node-debug.d.ts", + "build": "npm run prebuild && npx webpack --config webpack.config.js && shx cp dist/index.d.ts dist/ace.d.ts", + "build-debug": "npm run prebuild && npx webpack --config webpack-debug.config.js && shx cp dist/index.d.ts dist/ace-debug.d.ts", "prebuild": "ts-node src/genRuleIndex.ts", "test": "npm run prebuild && karma start", "serve": "serve .",