import { lowerToCanonical, type CanonicalClauseDescriptor } from 'imhotep-dsl' import type { FluentRelation } from 'imhotep-dsl' import type { ImhotepUi } from './public.js' export interface PresetResult { assertions: FluentRelation[] clauses: CanonicalClauseDescriptor[] } function toPresetResult(assertions: FluentRelation[]): PresetResult { const clauses: CanonicalClauseDescriptor[] = [] for (const assertion of assertions) { clauses.push(...lowerToCanonical(assertion.toAst())) } return { assertions, clauses } } export function touchTarget( ui: ImhotepUi, selector: string, options: { minSize?: number } = {}, ): PresetResult { const minSize = options.minSize ?? 44 const assertions = [ ui.expect(selector).to.be.atLeast(minSize, 'width'), ui.expect(selector).to.be.atLeast(minSize, 'height'), ] return toPresetResult(assertions) } export function toolbarAlignment( ui: ImhotepUi, selectors: string[], options: { minGap?: number; axis?: 'centerY' | 'centerX' } = {}, ): PresetResult { const minGap = options.minGap ?? 8 const axis = options.axis ?? 'centerY' const assertions: FluentRelation[] = [] for (let i = 0; i < selectors.length - 1; i++) { assertions.push(ui.expect(selectors[i]).to.be.leftOf(selectors[i + 1], { minGap })) assertions.push(ui.expect(selectors[i]).to.be.alignedWith(selectors[i + 1], { axis })) } return toPresetResult(assertions) } export function cardSpacing( ui: ImhotepUi, cardSelector: string, nextCardSelector: string, options: { minGap?: number } = {}, ): PresetResult { const minGap = options.minGap ?? 16 const assertions = [ ui.expect(cardSelector).to.be.leftOf(nextCardSelector, { minGap }), ui.expect(cardSelector).to.be.alignedWith(nextCardSelector, { axis: 'top' }), ] return toPresetResult(assertions) } export function formLabelAlignment( ui: ImhotepUi, labelSelector: string, inputSelector: string, options: { labelAbove?: boolean; maxGap?: number } = {}, ): PresetResult { const labelAbove = options.labelAbove ?? true const maxGap = options.maxGap ?? 16 const assertions: FluentRelation[] = [] if (labelAbove) { assertions.push(ui.expect(labelSelector).to.be.above(inputSelector, { maxGap })) assertions.push(ui.expect(labelSelector).to.be.alignedWith(inputSelector, { axis: 'left' })) } else { assertions.push(ui.expect(labelSelector).to.be.leftOf(inputSelector, { maxGap })) assertions.push(ui.expect(labelSelector).to.be.alignedWith(inputSelector, { axis: 'centerY' })) } return toPresetResult(assertions) } export function modalContainment( ui: ImhotepUi, modalSelector: string, ): PresetResult { const assertions = [ ui.expect(modalSelector).to.be.inside('body'), ui.expect(modalSelector).to.be.centeredWithin('body'), ] return toPresetResult(assertions) }