import { Diagnostic , Rule } from "@siteimprove/alfa-act" ; import { Node , Role } from "@siteimprove/alfa-aria" ; import { Element , Namespace } from "@siteimprove/alfa-dom" ; import { Iterable } from "@siteimprove/alfa-iterable" ; import { Predicate } from "@siteimprove/alfa-predicate" ; import { Err , Ok } from "@siteimprove/alfa-result" ; import { Page } from "@siteimprove/alfa-web" ; import { expectation } from "../common/expectation" ; import { Group } from "../common/group" ; import { normalize } from "../common/normalize" ; import { hasRole , isIgnored } from "../common/predicate" ; import { Question } from "../common/question" ; import { Scope } from "../tags" ; const { and , equals , not } = Predicate ; const { hasNamespace } = Element ; export default Rule . Atomic . of < Page , Group < Element > , Question . Metadata > ( { uri : "https://alfa.siteimprove.com/rules/sia-r55" , tags : [ Scope . Component ] , evaluate ( { device , document } ) { return { applicability ( ) { return ( document . descendants ( { flattened : true , nested : true } ) . filter ( Element . isElement ) . filter ( and ( hasNamespace ( equals ( Namespace . HTML ) ) , not ( isIgnored ( device ) ) , hasRole ( device , ( role ) => role . is ( "landmark" ) ) ) ) . groupBy ( ( landmark ) => Node . from ( landmark , device ) . name . map ( ( name ) => normalize ( name . value ) ) ) . filter ( ( landmarks ) => landmarks . size > 1 ) . flatMap ( ( sameName ) => sameName . groupBy ( ( landmark ) => Node . from ( landmark , device ) . role . get ( ) ) ) . filter ( ( elements ) => elements . size > 1 ) . map ( Group . of ) . values ( ) ) ; } , expectations ( target ) { const role = Node . from ( Iterable . first ( target ) . get ( ) , device ) . role . get ( ) . name ; const sameResource = Question . of ( "is-content-equivalent" , target , Diagnostic . of ( `Do these ${ role } landmarks have the same or equivalent content?` ) ) ; return { 1 : sameResource . map ( ( same ) => expectation ( same , ( ) => Outcomes . SameResource ( role ) , ( ) => Outcomes . DifferentResources ( role ) ) ) , } ; } , } ; } , } ) ; export namespace Outcomes { export const SameResource = ( role ) => Ok . of ( WithRole . of ( `No two \`${ role }\` have the same name and different content.` , role ) ) ; export const DifferentResources = ( role ) => Err . of ( WithRole . of ( `Some \`${ role }\` have the same name and different content.` , role ) ) ; } class WithRole extends Diagnostic { public static of ( message , role = "none" ) { return new WithRole ( message , role ) ; } private readonly _role : Role . Name ; private constructor ( message , role ) { super ( message ) ; this . _role = role ; } public get role ( ) : Role . Name { return this . _role ; } public equals ( value ) ; public equals ( value ) ; public equals ( value ) { return ( value instanceof WithRole && value . _message === this . _message && value . _role === this . _role ) ; } public toJSON ( ) { return { ... super . toJSON ( ) , role : this . _role , } ; } } namespace WithRole { export interface JSON extends Diagnostic . JSON { role : string ; } export function isWithRole ( value ) { return value instanceof WithRole ; } }