From 78264ee663d528bc3fbfc9ea7dbba180259f01af Mon Sep 17 00:00:00 2001 From: Wilco Fiers Date: Wed, 29 Mar 2023 10:55:02 +0200 Subject: [PATCH] feat(d.ts): setup/teardown, reporters & metadata definitions (#3966) --- axe.d.ts | 83 +++++++++++++++++++++++++++--- typings/axe-core/axe-core-tests.ts | 76 ++++++++++++++++++++------- 2 files changed, 134 insertions(+), 25 deletions(-) diff --git a/axe.d.ts b/axe.d.ts index e9a447e4ae..c174b24c0f 100644 --- a/axe.d.ts +++ b/axe.d.ts @@ -190,12 +190,13 @@ declare namespace axe { help: string; }; } + interface CheckMessages { + pass: string | { [key: string]: string }; + fail: string | { [key: string]: string }; + incomplete: string | { [key: string]: string }; + } interface CheckLocale { - [key: string]: { - pass: string | { [key: string]: string }; - fail: string | { [key: string]: string }; - incomplete: string | { [key: string]: string }; - }; + [key: string]: CheckMessages; } interface Locale { lang?: string; @@ -237,7 +238,7 @@ declare namespace axe { } interface Spec { branding?: string | Branding; - reporter?: ReporterVersion; + reporter?: ReporterVersion | string | AxeReporter; checks?: Check[]; rules?: Rule[]; standards?: Standards; @@ -263,6 +264,10 @@ declare namespace axe { options?: any; matches?: string; enabled?: boolean; + metadata?: { + impact?: ImpactValue; + messages?: CheckMessages; + }; } interface Rule { id: string; @@ -277,6 +282,7 @@ declare namespace axe { tags?: string[]; matches?: string; reviewOnFail?: boolean; + metadata?: Omit; } interface AxePlugin { id: string; @@ -319,6 +325,40 @@ declare namespace axe { frameSelector: CrossTreeSelector; frameContext: FrameContextObject; } + + interface RawNodeResult { + any: CheckResult[]; + all: CheckResult[]; + none: CheckResult[]; + impact: ImpactValue | null; + result: T; + } + + interface RawResult extends Omit { + inapplicable: []; + passes: RawNodeResult<'passed'>[]; + incomplete: RawNodeResult<'incomplete'>[]; + violations: RawNodeResult<'failed'>[]; + pageLevel: boolean; + result: 'failed' | 'passed' | 'incomplete' | 'inapplicable'; + } + + type AxeReporter = ( + rawResults: RawResult[], + option: RunOptions, + callback: (report: T) => void + ) => void; + + interface VirtualNode { + actualNode?: Node; + shadowId?: string; + children?: VirtualNode[]; + parent?: VirtualNode; + attr(attr: string): string | null; + hasAttr(attr: string): boolean; + props: { [key: string]: unknown }; + } + interface Utils { getFrameContexts: ( context?: ElementContext, @@ -326,7 +366,9 @@ declare namespace axe { ) => FrameContext[]; shadowSelect: (selector: CrossTreeSelector) => Element | null; shadowSelectAll: (selector: CrossTreeSelector) => Element[]; + getStandards(): Required; } + interface EnvironmentData { testEngine: TestEngine; testRunner: TestRunner; @@ -436,6 +478,35 @@ declare namespace axe { */ function frameMessenger(frameMessenger: FrameMessenger): void; + /** + * Setup axe-core so axe.common functions can work properly. + */ + function setup(node?: Element | Document): VirtualNode; + + /** + * Clean up axe-core tree and caches. `axe.run` will call this function at the end of the run so there's no need to call it yourself afterwards. + */ + function teardown(): void; + + /** + * Check if a reporter is registered + */ + function hasReporter(reporterName: string): boolean; + + /** + * Get a reporter based the name it is registered with + */ + function getReporter(reporterName: string): AxeReporter; + + /** + * Register a new reporter, optionally setting it as the default + */ + function addReporter( + reporterName: string, + reporter: AxeReporter, + isDefault?: boolean + ): void; + // axe.frameMessenger type FrameMessenger = { open: (topicHandler: TopicHandler) => Close | void; diff --git a/typings/axe-core/axe-core-tests.ts b/typings/axe-core/axe-core-tests.ts index ba83d9fd5f..309dc9e75a 100644 --- a/typings/axe-core/axe-core-tests.ts +++ b/typings/axe-core/axe-core-tests.ts @@ -223,10 +223,22 @@ var spec: axe.Spec = { id: 'custom-check', evaluate: function () { return true; + }, + metadata: { + impact: 'minor', + messages: { + pass: 'yes', + fail: 'nope', + incomplete: { + maybe: 'maybe', + or: 'maybe not' + } + } } } ], standards: { + ...axe.utils.getStandards(), ariaRoles: { 'custom-role': { type: 'widget', @@ -251,7 +263,13 @@ var spec: axe.Spec = { rules: [ { id: 'custom-rule', - any: ['custom-check'] + any: ['custom-check'], + metadata: { + description: 'custom rule', + help: 'different help', + helpUrl: 'https://example.com', + tags: ['custom'] + } } ] }; @@ -270,24 +288,6 @@ rules.forEach(rule => { rule.ruleId.substr(1234); }); -// Plugins -var pluginSrc: axe.AxePlugin = { - id: 'doStuff', - run: (data: any, callback: Function) => { - callback(); - }, - commands: [ - { - id: 'run-doStuff', - callback: (data: any, callback: Function) => { - axe.plugins['doStuff'].run(data, callback); - } - } - ] -}; -axe.registerPlugin(pluginSrc); -axe.cleanup(); - axe.configure({ locale: { checks: { @@ -322,3 +322,41 @@ axe.configure({ } } }); + +// Reporters +let fooReporter = ( + results: axe.RawResult[], + options: axe.RunOptions, + cb: (out: 'foo') => void +) => { + cb('foo'); +}; + +axe.addReporter<'foo'>('foo', fooReporter, true); +axe.configure({ reporter: fooReporter }); +fooReporter = axe.getReporter<'foo'>('foo'); +const hasFoo: boolean = axe.hasReporter('foo'); + +// setup & teardown +axe.setup(); +axe.setup(document); +axe.setup(document.createElement('div')); +axe.teardown(); + +// Plugins +var pluginSrc: axe.AxePlugin = { + id: 'doStuff', + run: (data: any, callback: Function) => { + callback(); + }, + commands: [ + { + id: 'run-doStuff', + callback: (data: any, callback: Function) => { + axe.plugins['doStuff'].run(data, callback); + } + } + ] +}; +axe.registerPlugin(pluginSrc); +axe.cleanup();