import { Assertions, Chain, GeneralSteps, Logger, Step, UiFinder } from '@ephox/agar'; import { UnitTest } from '@ephox/bedrock-client'; import { Fun } from '@ephox/katamari'; import { SugarNode } from '@ephox/sugar'; import * as Behaviour from 'ephox/alloy/api/behaviour/Behaviour'; import { Sandboxing } from 'ephox/alloy/api/behaviour/Sandboxing'; import type { AlloySpec } from 'ephox/alloy/api/component/SpecTypes'; import * as AlloyTriggers from 'ephox/alloy/api/events/AlloyTriggers'; import * as SystemEvents from 'ephox/alloy/api/events/SystemEvents'; import { Container } from 'ephox/alloy/api/ui/Container'; import { Input } from 'ephox/alloy/api/ui/Input'; import * as GuiSetup from 'ephox/alloy/test/GuiSetup'; import * as Sinks from 'ephox/alloy/test/Sinks'; UnitTest.asynctest('SandboxingTest', (success, failure) => { GuiSetup.setup((_store, _doc, _body) => Sinks.fixedSink(), (_doc, _body, gui, sink, store) => { const sandbox = sink.getSystem().build( Container.sketch({ dom: { classes: [ 'test-sandbox' ] }, uid: 'no-duplicates', containerBehaviours: Behaviour.derive([ Sandboxing.config({ getAttachPoint: (c) => { Assertions.assertEq('Checking getAttachPoint gets given sandbox', sandbox.element, c.element); return sink; }, onOpen: store.adder('onOpen'), onClose: store.adder('onClose'), isPartOf: Fun.never }) ]) }) ); const sOpenWith = (data: AlloySpec) => Step.sync(() => { Sandboxing.open(sandbox, data); }); const sClose = Step.sync(() => { Sandboxing.close(sandbox); }); const sCheckShowing = (label: string, expected: boolean) => Step.sync(() => { Assertions.assertEq( label + '\nSandbox should ' + (expected === false ? '*not* ' : '') + 'be open', expected, Sandboxing.isOpen(sandbox) ); }); const sCheckOpenState = (label: string, expected: { data: string; store: string[] }) => Logger.t( label, GeneralSteps.sequence([ sCheckShowing(label, true), UiFinder.sExists(gui.element, 'input[data-test-input="' + expected.data + '"]'), UiFinder.sExists(gui.element, '.test-sandbox'), store.sAssertEq('Checking store', expected.store), store.sClear, Step.sync(() => { const state = Sandboxing.getState(sandbox); Assertions.assertEq(label + '\nChecking state node name', 'input', SugarNode.name(state.getOrDie().element)); }) ]) ); const sCheckClosedState = (label: string, expected: { store: string[] }) => Logger.t( label, GeneralSteps.sequence([ sCheckShowing(label, false), UiFinder.sNotExists(gui.element, 'input[data-test-input]'), UiFinder.sNotExists(gui.element, '.test-sandbox'), store.sAssertEq(label, expected.store), store.sClear, Step.sync(() => { const state = Sandboxing.getState(sandbox); Assertions.assertEq(label + '\nChecking state is not set', true, state.isNone()); }) ]) ); const makeData = (rawData: string) => Input.sketch({ uid: rawData, inputAttributes: { 'data-test-input': rawData } }); const firstOpening = makeData('first-opening'); const secondOpening = makeData('second-opening'); return [ // initially sCheckClosedState('Initial state', { store: [ ] }), // opening sandbox Logger.t('Opening sandbox', sOpenWith(firstOpening)), sCheckOpenState('Opening sandbox', { data: 'first-opening', store: [ 'onOpen' ] }), // opening sandbox again Logger.t('Opening sandbox while it is already open', sOpenWith(secondOpening)), sCheckOpenState('Opening sandbox while it is already open', { data: 'second-opening', store: [ 'onOpen' ] }), // closing sandbox again Logger.t('Closing sandbox 2', sClose), sCheckClosedState('After closing 2', { store: [ 'onClose' ] }), Logger.t('Opening sandbox with a uid that has already been used: try and re-open firstOpening', sOpenWith(firstOpening)), sCheckOpenState('Opening sandbox with a uid that has already been used', { data: 'first-opening', store: [ 'onOpen' ] }), Logger.t( 'Firing sandbox close system event', Chain.asStep({}, [ Chain.inject(sandbox.element), UiFinder.cFindIn('input'), Chain.op((input) => { AlloyTriggers.dispatch(sandbox, input, SystemEvents.sandboxClose()); }) ]) ), sCheckClosedState('After sending system close event', { store: [ 'onClose' ] }) ]; }, success, failure); });