import { Assertions, Chain, Mouse, Step, UiFinder } from '@ephox/agar'; import { Arr, Fun } from '@ephox/katamari'; import { SugarElement, SugarShadowDom, Visibility } from '@ephox/sugar'; import { Editor } from '../../alien/EditorTypes'; import * as TinyUiActions from '../bdd/TinyUiActions'; import { getThemeSelectors } from '../ThemeSelectors'; export interface TinyUi { clickOnToolbar: (elector: string) => void; clickOnMenu: (selector: string) => void; clickOnUi: (selector: string) => void; submitDialog: (selector: string) => void; pWaitForUi: (selector: string) => Promise>; pWaitForPopup: (selector: string) => Promise>; sClickOnToolbar: (label: string, selector: string) => Step; sClickOnMenu: (label: string, selector: string) => Step; sClickOnUi: (label: string, selector: string) => Step; sWaitForUi: (label: string, selector: string) => Step; sWaitForPopup: (label: string, selector: string) => Step; sFillDialogWith: (data: Record, selector: string) => Step; sSubmitDialog: (selector: string) => Step; cWaitForPopup: (label: string, selector: string) => Chain>; cWaitForUi: (label: string, selector: string) => Chain>; cWaitForState: (hasState: (element: SugarElement) => boolean) => (label: string, selector: string) => Chain>; cFillDialogWith: (data: Record) => Chain, SugarElement>; cSubmitDialog: () => Chain, SugarElement>; cAssertDialogContents: (data: Record) => Chain, SugarElement>; cTriggerContextMenu: (label: string, target: string, menu: string) => Chain>; } export const TinyUi = (editor: Editor): TinyUi => { const uiRoot = SugarShadowDom.getContentContainer(SugarShadowDom.getRootNode(SugarElement.fromDom(editor.getElement()))); const editorRoot = SugarElement.fromDom(editor.getBody()); const clickOnToolbar = Fun.curry(TinyUiActions.clickOnToolbar, editor); const clickOnMenu = Fun.curry(TinyUiActions.clickOnMenu, editor); const clickOnUi = Fun.curry(TinyUiActions.clickOnUi, editor); const submitDialog = Fun.curry(TinyUiActions.submitDialog, editor); const pWaitForUi = Fun.curry(TinyUiActions.pWaitForUi, editor); const pWaitForPopup = Fun.curry(TinyUiActions.pWaitForPopup, editor); const cUiRoot = Chain.inject(uiRoot); const cEditorRoot = Chain.inject(editorRoot); const cFindIn = (cRoot: Chain, SugarElement>, selector: string) => { return Chain.fromChains([ cRoot, UiFinder.cFindIn(selector) ]); }; const sClickOnToolbar = (label: string, selector: string) => Step.label(label, Step.sync(() => { TinyUiActions.clickOnToolbar(editor, selector); })); const sClickOnMenu = (label: string, selector: string) => Step.label(label, Step.sync(() => { TinyUiActions.clickOnMenu(editor, selector); })); const sClickOnUi = (label: string, selector: string) => Step.label(label, Step.sync(() => { TinyUiActions.clickOnUi(editor, selector); })); const sWaitForUi = (label: string, selector: string) => { return Chain.asStep({}, [ cWaitForUi(label, selector) ]); }; const sWaitForPopup = (label: string, selector: string) => { return Chain.asStep({}, [ cWaitForPopup(label, selector) ]); }; const cWaitForState = (hasState: (element: SugarElement) => boolean) => { return (label: string, selector: string): Chain> => { return Chain.fromChainsWith(uiRoot, [ UiFinder.cWaitForState(label, selector, hasState) ]); }; }; const cWaitForPopup = (label: string, selector: string) => { return cWaitForState(Visibility.isVisible)(label, selector); }; const cWaitForUi = (label: string, selector: string) => { return cWaitForState(Fun.always)(label, selector); }; const cTriggerContextMenu = (label: string, target: string, menu: string): Chain> => { return Chain.fromChains([ cFindIn(cEditorRoot, target), Mouse.cContextMenu, // Ignores input cWaitForPopup(label, menu) ]); }; const getDialogByElement = (element: SugarElement) => { return Arr.find(editor.windowManager.getWindows(), (win: any) => { return element.dom.id === win._id; }); }; const cAssertDialogContents = (data: Record) => { return Chain.async, SugarElement>((element, next, die) => { getDialogByElement(element).fold(() => die('Can not find dialog'), (win) => { Assertions.assertEq('asserting dialog contents', data, win.toJSON()); next(element); }); }); }; const cFillDialogWith = (data: Record) => { return Chain.async, SugarElement>((element, next, die) => { getDialogByElement(element).fold(() => die('Can not find dialog'), (win) => { win.fromJSON({ ...win.toJSON(), ...data }); next(element); }); }); }; const sFillDialogWith = (data: Record, selector: string) => { return Chain.asStep({}, [ cFindIn(cUiRoot, selector), cFillDialogWith(data) ]); }; const cSubmitDialog = () => { return Chain.fromChains, SugarElement>([ Chain.binder((container: SugarElement) => UiFinder.findIn(container, getThemeSelectors().dialogSubmitSelector)), Mouse.cClick ]); }; const sSubmitDialog = (selector: string) => { return Chain.asStep({}, [ cFindIn(cUiRoot, selector), cSubmitDialog() ]); }; return { clickOnToolbar, clickOnMenu, clickOnUi, submitDialog, pWaitForUi, pWaitForPopup, sClickOnToolbar, sClickOnMenu, sClickOnUi, // Popups need to be visible. cWaitForPopup, // UI does not need to be visible cWaitForUi, // General state predicate cWaitForState, cFillDialogWith, sFillDialogWith, cSubmitDialog, sSubmitDialog, cAssertDialogContents, cTriggerContextMenu, sWaitForUi, sWaitForPopup }; };