import { Result } from '@ephox/katamari'; import { Compare, Focus, type SugarElement, SugarShadowDom, Truncate } from '@ephox/sugar'; import * as SizzleFind from '../alien/SizzleFind'; import { Chain } from './Chain'; import * as Guard from './Guard'; import * as Logger from './Logger'; import { Step } from './Step'; import * as UiControls from './UiControls'; import * as UiFinder from './UiFinder'; import * as Waiter from './Waiter'; const getFocused = (doc: SugarElement): Result, string> => { return Focus.active(doc).fold( () => Result.error('Could not find active element'), Result.value ); }; const getActiveValue = (element: SugarElement): string | undefined => { const doc = SugarShadowDom.getRootNode(element); const focused = getFocused(doc).getOrDie(); return UiControls.getValue(focused as SugarElement); }; const setFocus = (container: SugarElement, selector: string): SugarElement => { const elem = UiFinder.findIn(container, selector).getOrDie(); Focus.focus(elem); return elem; }; const setActiveValue = (doc: SugarElement, newValue: string, eventName: string = 'input'): SugarElement => { const focused = getFocused(doc).getOrDie(); UiControls.setValue(focused as SugarElement, newValue, eventName); return focused; }; const isOn = (label: string, element: SugarElement): SugarElement => { const doc = SugarShadowDom.getRootNode(element); return getFocused(doc).bind((active) => { return Compare.eq(element, active) ? Result.value(active) : Result.error( label + '\nExpected focus: ' + Truncate.getHtml(element) + '\nActual focus: ' + Truncate.getHtml(active) ); }).getOrDie(); }; const isOnSelector = (label: string, doc: SugarElement, selector: string): SugarElement => { return getFocused(doc).bind((active) => { return SizzleFind.matches(active, selector) ? Result.value(active) : Result.error( label + '\nExpected focus $("' + selector + '")]\nActual focus: ' + Truncate.getHtml(active) ); }).getOrDie(); }; const isOnByLabel = (errorLabel: string, doc: SugarElement, label: string): SugarElement => { const element = UiFinder.findTargetByLabel(doc, label).getOrDie(); return getFocused(doc).bind((active) => { return Compare.eq(element, active) ? Result.value(active) : Result.error( errorLabel + '\nExpected focus: ' + Truncate.getHtml(element) + '\nActual focus: ' + Truncate.getHtml(active) ); }).getOrDie(); }; const cGetFocused: Chain, SugarElement> = Chain.binder(getFocused); const cGetRootNode: Chain, SugarElement> = Chain.mapper(SugarShadowDom.getRootNode); const wrapInResult = (f: () => R) => (): Result => { try { return Result.value(f()); } catch (e) { return Result.error(e.message); } }; const sIsOn = (label: string, element: SugarElement): Step => Chain.asStep>(element, [ Chain.binder(wrapInResult(() => isOn(label, element))) ]); const sIsOnSelector = (label: string, doc: SugarElement, selector: string): Step => Logger.t( `${label}: sIsOnSelector(${selector})`, Chain.asStep>(doc, [ Chain.binder(wrapInResult(() => isOnSelector(label, doc, selector))) ]) ); const sTryOnSelector = (label: string, doc: SugarElement, selector: string, interval: number = 10, amount: number = 3000): Step => Logger.t( label + '. Focus did not match: ' + selector, Waiter.sTryUntil( 'Waiting for focus', sIsOnSelector(label, doc, selector), interval, amount ) ); const pTryOnSelector = (label: string, doc: SugarElement, selector: string): Promise> => Waiter.pTryUntil(label + '. Focus did not match: ' + selector, () => isOnSelector(label, doc, selector)); const pTryOnByLabel = (errorLabel: string, doc: SugarElement, label: string): Promise> => Waiter.pTryUntil(errorLabel + '. Focus did not match label: ' + label, () => isOnByLabel(errorLabel, doc, label)); const cSetFocus = (label: string, selector: string): Chain, SugarElement> => // Input: container Chain.control( Chain.mapper((container) => setFocus(container, selector)), Guard.addLogging(label) ); const cSetActiveValue = (newValue: string): Chain, SugarElement> => // Input: container Chain.fromChains([ cGetRootNode, Chain.mapper((root) => setActiveValue(root, newValue)) ]); // Input: container const cGetActiveValue: Chain, string> = Chain.fromChains([ cGetRootNode, cGetFocused, UiControls.cGetValue ]); const sSetFocus = (label: string, container: SugarElement, selector: string): Step => Chain.asStep>(container, [ cSetFocus(label, selector) ]); const sSetActiveValue = (doc: SugarElement, newValue: string): Step => Step.sync(() => setActiveValue(doc, newValue)); export { getActiveValue, setActiveValue, setFocus, getFocused, isOn, isOnSelector, isOnByLabel, pTryOnSelector, pTryOnByLabel, sSetActiveValue, sSetFocus, sIsOn, sIsOnSelector, sTryOnSelector, cSetFocus, cSetActiveValue, cGetActiveValue, cGetFocused };