import { Diagnostic , Rule } from "@siteimprove/alfa-act" ; import { Node } from "@siteimprove/alfa-aria" ; import { Element , Namespace } from "@siteimprove/alfa-dom" ; import { Predicate } from "@siteimprove/alfa-predicate" ; import { Refinement } from "@siteimprove/alfa-refinement" ; import { Err , Ok } from "@siteimprove/alfa-result" ; import { Sequence } from "@siteimprove/alfa-sequence" ; import { Page } from "@siteimprove/alfa-web" ; import { expectation } from "../common/expectation" ; import { getNodesBetween } from "../common/expectation/get-nodes-between" ; import { hasHeadingLevel , hasRole , isContent , isIgnored , isPerceivable , } from "../common/predicate" ; import { Scope } from "../tags" ; const { hasNamespace , isElement } = Element ; const { not } = Predicate ; const { and } = Refinement ; export default Rule . Atomic . of < Page , Element > ( { uri : "https://alfa.siteimprove.com/rules/sia-r78" , tags : [ Scope . Component ] , evaluate ( { device , document } ) { let headings ; return { applicability ( ) { headings = document . descendants ( { flattened : true , nested : true } ) . filter ( and ( isElement , and ( hasNamespace ( Namespace . HTML ) , not ( isIgnored ( device ) ) , hasRole ( device , "heading" ) ) ) ) ; return headings ; } , expectations ( target ) { const currentLevel = Node . from ( target , device ) . attribute ( "aria-level" ) . map ( ( level ) => Number ( level . value ) ) . getOr ( 0 ) ; let end = false ; const next = headings . skipUntil ( ( heading ) => heading . equals ( target ) ) . rest ( ) . find ( hasHeadingLevel ( device , ( level ) => level <= currentLevel ) ) . getOrElse ( ( ) => { end = true ; return ( document . descendants ( { flattened : true , nested : true } ) . last ( ) . get ( ) ) ; } ) ; return { 1 : expectation ( getNodesBetween ( target , next , { includeFirst : false , includeSecond : end , } ) . some ( and ( isPerceivable ( device ) , isContent ( { flattened : true , nested : true } ) ) ) , ( ) => Outcomes . hasContent , ( ) => Outcomes . hasNoContent ) , } ; } , } ; } , } ) ; export namespace Outcomes { export const hasContent = Ok . of ( Diagnostic . of ( "There is content between this heading and the next" ) ) ; export const hasNoContent = Err . of ( Diagnostic . of ( "There is no content between this heading and the next" ) ) ; }