From 6bd7526c8f11d64d9f8ea57f83cff28c3ead71eb Mon Sep 17 00:00:00 2001 From: Zsofia Toth Date: Mon, 20 Jun 2022 17:25:28 +0200 Subject: [PATCH 01/10] SIA-ER65: Split result to show why we pass --- .../alfa-rules/src/sia-er65/diagnostics.ts | 67 +++++++++++++++++++ packages/alfa-rules/src/sia-er65/rule.ts | 39 ++++++++--- .../alfa-rules/test/sia-er65/rule.spec.tsx | 66 +++++++++--------- packages/alfa-rules/tsconfig.json | 1 + 4 files changed, 130 insertions(+), 43 deletions(-) create mode 100644 packages/alfa-rules/src/sia-er65/diagnostics.ts diff --git a/packages/alfa-rules/src/sia-er65/diagnostics.ts b/packages/alfa-rules/src/sia-er65/diagnostics.ts new file mode 100644 index 0000000000..5a8ed29928 --- /dev/null +++ b/packages/alfa-rules/src/sia-er65/diagnostics.ts @@ -0,0 +1,67 @@ +import { Diagnostic } from "@siteimprove/alfa-act"; + +type Reason = "focus-indicator" | "class"; +export type Detection = "auto" | "manual"; + +/** + * @internal + */ +export class ExtendedDiagnostic extends Diagnostic { + public static of( + message: string, + reason: Reason = "class", + detection: Detection = "auto" + ): ExtendedDiagnostic { + return new ExtendedDiagnostic(message, reason, detection); + } + + private readonly _reason: Reason; + private readonly _detection: Detection; + + private constructor(message: string, reason: Reason, detection: Detection) { + super(message); + this._reason = reason; + this._detection = detection; + } + + public equals(value: ExtendedDiagnostic): boolean; + + public equals(value: unknown): value is this; + + public equals(value: unknown): boolean { + return ( + value instanceof ExtendedDiagnostic && + value._reason === this._reason && + value._detection === this._detection + ); + } + + public toJSON(): ExtendedDiagnostic.JSON { + return { + ...super.toJSON(), + reason: this._reason, + detection: this._detection, + }; + } +} + +/** + * @internal + */ +export namespace ExtendedDiagnostic { + export interface JSON extends Diagnostic.JSON {} + + export function isExtendedDiagnostic( + value: Diagnostic + ): value is ExtendedDiagnostic; + + export function isExtendedDiagnostic( + value: unknown + ): value is ExtendedDiagnostic; + + export function isExtendedDiagnostic( + value: unknown + ): value is ExtendedDiagnostic { + return value instanceof ExtendedDiagnostic; + } +} diff --git a/packages/alfa-rules/src/sia-er65/rule.ts b/packages/alfa-rules/src/sia-er65/rule.ts index 7ecedc57d9..9a16eb7ccb 100755 --- a/packages/alfa-rules/src/sia-er65/rule.ts +++ b/packages/alfa-rules/src/sia-er65/rule.ts @@ -1,4 +1,4 @@ -import { Rule, Diagnostic } from "@siteimprove/alfa-act"; +import { Rule } from "@siteimprove/alfa-act"; import { Keyword } from "@siteimprove/alfa-css"; import { Device } from "@siteimprove/alfa-device"; import { Document, Element } from "@siteimprove/alfa-dom"; @@ -16,6 +16,8 @@ import { expectation } from "../common/act/expectation"; import { Question } from "../common/act/question"; import { Scope, Stability, Version } from "../tags"; +import { ExtendedDiagnostic, Detection } from "./diagnostics"; + const { isElement } = Element; const { isKeyword } = Keyword; const { or, test, xor } = Predicate; @@ -59,6 +61,9 @@ export default Rule.Atomic.of< ); const askFocusIndicator = Question.of("has-focus-indicator", target); + const detection: Detection = hasFocusIndicator(device)(target) + ? "auto" + : "manual"; return { 1: askGoodClasses @@ -77,8 +82,8 @@ export default Rule.Atomic.of< .map((hasFocusIndicator) => expectation( hasFocusIndicator, - () => Outcomes.HasFocusIndicator, - () => Outcomes.HasNoFocusIndicator + () => Outcomes.HasFocusIndicator(detection), + () => Outcomes.HasNoFocusIndicator(detection) ) ) ) @@ -90,16 +95,30 @@ export default Rule.Atomic.of< }); export namespace Outcomes { - export const HasFocusIndicator = Ok.of( - Diagnostic.of(`The element has a visible focus indicator`) - ); + export const HasFocusIndicator = (detection: Detection) => + Ok.of( + ExtendedDiagnostic.of( + `The element has a visible focus indicator`, + "focus-indicator", + detection + ) + ); - export const HasNoFocusIndicator = Err.of( - Diagnostic.of(`The element does not have a visible focus indicator`) - ); + export const HasNoFocusIndicator = (detection: Detection) => + Err.of( + ExtendedDiagnostic.of( + `The element does not have a visible focus indicator`, + "focus-indicator", + detection + ) + ); export const HasGoodClass = Ok.of( - Diagnostic.of("The element has a class ensuring a visible focus indicator") + ExtendedDiagnostic.of( + "The element has a class ensuring a visible focus indicator", + "class", + "auto" + ) ); } diff --git a/packages/alfa-rules/test/sia-er65/rule.spec.tsx b/packages/alfa-rules/test/sia-er65/rule.spec.tsx index 71cbd76c23..3f253ce78f 100755 --- a/packages/alfa-rules/test/sia-er65/rule.spec.tsx +++ b/packages/alfa-rules/test/sia-er65/rule.spec.tsx @@ -14,10 +14,10 @@ test(`evaluate() passes an element that uses the default focus outline`, asy t.deepEqual(await evaluate(R65, { document }), [ passed(R65, target, { - 1: Outcomes.HasFocusIndicator, + 1: Outcomes.HasFocusIndicator("auto"), }), passed(R65,