diff --git a/src/test/ts/browser/EditorBehaviorTest.ts b/src/test/ts/browser/EditorBehaviorTest.ts index 32561a43..ac5e5624 100644 --- a/src/test/ts/browser/EditorBehaviorTest.ts +++ b/src/test/ts/browser/EditorBehaviorTest.ts @@ -1,20 +1,20 @@ -import { Assertions, Chain, Logger, Pipeline, GeneralSteps } from '@ephox/agar'; -import { UnitTest } from '@ephox/bedrock-client'; -import { cRemove, cRender, cEditor, cReRender } from '../alien/Loader'; -import { VersionLoader } from '@tinymce/miniature'; +import { render } from '../alien/Loader'; import { PlatformDetection } from '@ephox/sand'; +import { context, describe, it } from '@ephox/bedrock-client'; + import { getTinymce } from '../../../main/ts/TinyMCE'; -import { EventStore, VERSIONS, cAssertContent, cSetContent, type Version } from '../alien/TestHelpers'; +import { EventStore, VERSIONS } from '../alien/TestHelpers'; import { Editor as TinyMCEEditor, EditorEvent, Events } from 'tinymce'; +import { Assertions } from '@ephox/agar'; +import { TinyAssertions } from '@ephox/mcagar'; type SetContentEvent = EditorEvent; -UnitTest.asynctest('EditorBehaviorTest', (success, failure) => { +describe('EditorBehaviourTest', () => { const browser = PlatformDetection.detect().browser; if (browser.isIE()) { // INT-2278: This test currently times out in IE so we are skipping it - success(); return; } const versionRegex = /6|7/; @@ -29,18 +29,18 @@ UnitTest.asynctest('EditorBehaviorTest', (success, failure) => { const eventStore = EventStore(); - const sTestVersion = (version: Version) => VersionLoader.sWithVersion( - version, - GeneralSteps.sequence([ - Logger.t('Assert structure of tinymce and tinymce-react events', Chain.asStep({}, [ - cRender({ + VERSIONS.forEach((version) => + context(`TinyMCE (${version})`, () => { + it('Assert structure of tinymce and tinymce-react events', async () => { + using ctx = await render({ + cloudChannel: version, onEditorChange: eventStore.createHandler('onEditorChange'), onSetContent: eventStore.createHandler('onSetContent') - }), + }); // tinymce native event // initial content is empty as editor does not have a value or initialValue - eventStore.cEach('onSetContent', (events) => { + eventStore.each('onSetContent', (events) => { // note that this difference in behavior in 5-6 may be a bug, the team is investigating Assertions.assertEq( 'First arg should be event from Tiny', @@ -48,87 +48,78 @@ UnitTest.asynctest('EditorBehaviorTest', (success, failure) => { events[0].editorEvent.content ); Assertions.assertEq('Second arg should be editor', true, isEditor(events[0].editor)); - }), - - eventStore.cClearState, - - cEditor(cSetContent('

Initial Content

')), - + }); + eventStore.clearState(); + + ctx.editor.setContent('

Initial Content

'); // tinymce native event - eventStore.cEach('onSetContent', (events) => { + eventStore.each('onSetContent', (events) => { Assertions.assertEq('onSetContent should have been fired once', 1, events.length); Assertions.assertEq('First arg should be event from Tiny', '

Initial Content

', events[0].editorEvent.content); Assertions.assertEq('Second arg should be editor', true, isEditor(events[0].editor)); - }), + }); // tinymce-react unique event - eventStore.cEach('onEditorChange', (events) => { + eventStore.each('onEditorChange', (events) => { Assertions.assertEq('First arg should be new content', '

Initial Content

', events[0].editorEvent); Assertions.assertEq('Second arg should be editor', true, isEditor(events[0].editor)); - }), + }); + eventStore.clearState(); + }); - eventStore.cClearState, - cRemove - ])), - - Logger.t('onEditorChange should only fire when the editors content changes', Chain.asStep({}, [ - cRender({ + it('onEditorChange should only fire when the editors content changes', async () => { + using ctx = await render({ + cloudChannel: version, onEditorChange: eventStore.createHandler('onEditorChange') - }), + }); - cEditor(cSetContent('

Initial Content

')), - cEditor(cSetContent('

Initial Content

')), // Repeat + ctx.editor.setContent('

Initial Content

'); + ctx.editor.setContent('

Initial Content

'); // Repeat - eventStore.cEach('onEditorChange', (events) => { + eventStore.each('onEditorChange', (events) => { Assertions.assertEq('onEditorChange should have been fired once', 1, events.length); - }), - - eventStore.cClearState, - cRemove - ])), + }); + eventStore.clearState(); + }); - Logger.t('Should be able to register an event handler after initial render', Chain.asStep({}, [ - cRender({ initialValue: '

Initial Content

' }), - cReRender({ onSetContent: eventStore.createHandler('onSetContent') }), + it('Should be able to register an event handler after initial render', async () => { + using ctx = await render({ cloudChannel: version, initialValue: '

Initial Content

' }); + await ctx.reRender({ onSetContent: eventStore.createHandler('onSetContent') }); - cEditor(cAssertContent('

Initial Content

')), - cEditor(cSetContent('

New Content

')), + TinyAssertions.assertContent(ctx.editor, '

Initial Content

'); + ctx.editor.setContent('

New Content

'); - eventStore.cEach('onSetContent', (events) => { + eventStore.each('onSetContent', (events) => { Assertions.assertEq('Should have bound handler, hence new content', '

New Content

', events[0].editorEvent.content); - }), + }); - eventStore.cClearState, - cRemove - ])), + eventStore.clearState(); + }); - Logger.t('Providing a new event handler and re-rendering should unbind old handler and bind new handler', Chain.asStep({}, [ - cRender({ onSetContent: eventStore.createHandler('InitialHandler') }), - eventStore.cEach('InitialHandler', (events) => { + it('Providing a new event handler and re-rendering should unbind old handler and bind new handler', async () => { + using ctx = await render({ cloudChannel: version, onSetContent: eventStore.createHandler('InitialHandler') }); + eventStore.each('InitialHandler', (events) => { Assertions.assertEq( 'Initial content is empty as editor does not have a value or initialValue', // note that this difference in behavior in 5-6 may be a bug, the team is investigating versionRegex.test(version) ? '


' : '', events[0].editorEvent.content); - }), - eventStore.cClearState, - cEditor(cSetContent('

Initial Content

')), + }); + eventStore.clearState(); + ctx.editor.setContent('

Initial Content

'); - cReRender({ onSetContent: eventStore.createHandler('NewHandler') }), - cEditor(cSetContent('

New Content

')), + await ctx.reRender({ onSetContent: eventStore.createHandler('NewHandler') }), + ctx.editor.setContent('

New Content

'); - eventStore.cEach('InitialHandler', (events) => { + eventStore.each('InitialHandler', (events) => { Assertions.assertEq('Initial handler should have been unbound, hence initial content', '

Initial Content

', events[0].editorEvent.content); }), - eventStore.cEach('NewHandler', (events) => { + eventStore.each('NewHandler', (events) => { Assertions.assertEq('New handler should have been bound, hence new content', '

New Content

', events[0].editorEvent.content); - }), + }) - eventStore.cClearState, - cRemove - ])), - ]) + eventStore.clearState(); + }); + }) ); - - Pipeline.async({}, VERSIONS.map(sTestVersion), success, failure); -}); \ No newline at end of file +}); diff --git a/src/test/ts/browser/EditorInitTest.ts b/src/test/ts/browser/EditorInitTest.ts index 3afe580b..4b69c2f2 100644 --- a/src/test/ts/browser/EditorInitTest.ts +++ b/src/test/ts/browser/EditorInitTest.ts @@ -1,103 +1,82 @@ -import { Assertions, Chain, GeneralSteps, Logger, Pipeline } from '@ephox/agar'; -import { UnitTest } from '@ephox/bedrock-client'; -import { VersionLoader } from '@tinymce/miniature'; +import { Assertions } from '@ephox/agar'; +import { context, describe, it } from '@ephox/bedrock-client'; -import { cRemove, cRender, cDOMNode, cEditor, cReRender } from '../alien/Loader'; -import { type Version, cAssertContent, VERSIONS } from '../alien/TestHelpers'; +import { VALID_API_KEY, VERSIONS } from '../alien/TestHelpers'; +import * as Loader from '../alien/Loader'; +import { TinyAssertions } from '@ephox/mcagar'; +import { IAllProps } from 'src/main/ts'; -UnitTest.asynctest('Editor.test', (success, failure) => { - const cAssertProperty = (propName: string, expected: string) => Chain.op((el: HTMLElement) => { - Assertions.assertEq(propName + ' should be ' + expected, expected, (el as unknown as Record)[propName]); - }); +function assertProperty, K extends keyof T>(obj: T, propName: K, expected: T[K]): void +function assertProperty(obj: {}, propName: string, expected: unknown): void; +function assertProperty(obj: Record, propName: string, expected: unknown): void { + Assertions.assertEq(propName.toString() + ' should be ' + expected, expected, obj[propName]); +} - const sTestVersion = (version: Version) => VersionLoader.sWithVersion( - version, - GeneralSteps.sequence([ - Logger.t('tagName prop changes element', GeneralSteps.sequence([ - Logger.t('it is div by default for inline', Chain.asStep({}, [ - cRender({ inline: true }), - cDOMNode(cAssertProperty('tagName', 'DIV')), - cRemove - ])), +describe('EditorInitTest', async () => { + VERSIONS.forEach((version) => + context(`TinyMCE (${version})`, async () => { + const defaultProps: IAllProps = { apiKey: VALID_API_KEY, cloudChannel: version }; + const render = (props: IAllProps = {}) => Loader.render({ ...defaultProps, ...props }); - Logger.t('can be set to inline in init', Chain.asStep({}, [ - cRender({ - init: { - inline: true - } - }), - cDOMNode(cAssertProperty('tagName', 'DIV')), - cRemove - ])), + context('tagName prop changes element', () => { + it('is div by default for inline', async () => { + using ctx = await render({ inline: true }); + assertProperty(ctx.DOMNode, 'tagName', 'DIV'); + }); - Logger.t('it can be changed to p', Chain.asStep({}, [ - cRender({ - inline: true, - tagName: 'p' - }), - cDOMNode(cAssertProperty('tagName', 'P')), - cRemove - ])), + it('can be set to inline in init', async () => { + using ctx = await render({ init: { inline: true }}); + assertProperty(ctx.DOMNode, 'tagName', 'DIV'); + }); - Logger.t('iframe editor does not change element', Chain.asStep({}, [ - cRender({ tagName: 'p' }), - cDOMNode(cAssertProperty('tagName', 'TEXTAREA')), - cRemove - ])) - ])), + it('can be changed to p', async () => { + using ctx = await render({ inline: true, tagName: 'p' }); + assertProperty(ctx.DOMNode, 'tagName', 'P'); + }); - Logger.t('id is set automatically if id prop not provided', GeneralSteps.sequence([ - Logger.t('is set normally if prop is provided', Chain.asStep({}, [ - cRender({ id: 'test' }), - cDOMNode(cAssertProperty('id', 'test')), - cRemove - ])), + it('iframe editor does not change element', async () => { + using ctx = await render({ tagName: 'p' }); + assertProperty(ctx.DOMNode, 'tagName', 'TEXTAREA'); + }); + }); - Logger.t('gets set automatically to uuid if not set', Chain.asStep({}, [ - cRender({}), - cDOMNode(Chain.op((node: Element) => { - Assertions.assertEq('Should not be uuid', typeof node.id === 'string' && node.id.indexOf('tiny-react') !== -1, true); - })), - cRemove - ])), - ])), + context('id is set automatically if id prop not provided', () => { + it('is set normally if prop is provided', async () => { + using ctx = await render({ id: 'test' }); + assertProperty(ctx.DOMNode, 'id', 'test'); + }); - Logger.t('sets name on form', GeneralSteps.sequence([ - Logger.t('is not set when prop is not provided', Chain.asStep({}, [ - cRender({}), - cDOMNode(cAssertProperty('name', '')), - cRemove - ])), + it('gets set automatically to uuid if not set', async () => { + using ctx = await render(); + Assertions.assertEq('Should not be uuid', typeof ctx.DOMNode.id === 'string' && ctx.DOMNode.id.indexOf('tiny-react') !== -1, true); + }); + }); - Logger.t('is set when prop is provided', Chain.asStep({}, [ - cRender({ textareaName: 'test' }), - cDOMNode(cAssertProperty('name', 'test')), - cRemove - ])), - ])), + context('sets name on form', () => { + it('is not set when prop is not provided', async () => { + using ctx = await render(); + assertProperty(ctx.DOMNode, 'name', ''); + }); - Logger.t('Value prop should propagate changes to editor', Chain.asStep({}, [ - cRender({ value: '

Initial Value

' }), - cEditor(cAssertContent('

Initial Value

')), - cReRender({ value: '

New Value

' }), - cEditor(cAssertContent('

New Value

')), - cRemove - ])), + it('is set when prop is provided', async () => { + using ctx = await render({ textareaName: 'test' }); + assertProperty(ctx.DOMNode, 'name', 'test'); + }); + }); - Logger.t('Disabled prop should disable editor', Chain.asStep({}, [ - cRender({}), - cEditor(Chain.op((editor) => { - Assertions.assertEq( - 'Should be design mode', true, version === '4' ? !editor.readonly : editor.mode.get() === 'design'); - })), - cReRender({ disabled: true }), - cEditor(Chain.op((editor) => { - Assertions.assertEq('Should be readonly mode', true, version === '4' ? editor.readonly : editor.mode.get() === 'readonly'); - })), - cRemove - ])) - ]) - ); + it('Value prop should propagate changes to editor', async () => { + using ctx = await render({ value: '

Initial Value

' }); + TinyAssertions.assertContent(ctx.editor, '

Initial Value

'); + ctx.reRender({ ...defaultProps, value: '

New Value

' }); + TinyAssertions.assertContent(ctx.editor, '

New Value

'); + }); - Pipeline.async({}, VERSIONS.map(sTestVersion), success, failure); -}); \ No newline at end of file + it('Disabled prop should disable editor', async () => { + using ctx = await render(); + Assertions.assertEq('Should be design mode', true, ['4'].includes(version) ? !ctx.editor.readonly : ctx.editor.mode.get() === 'design'); + ctx.reRender({ ...defaultProps, disabled: true }); + Assertions.assertEq('Should be readonly mode', true, ['4'].includes(version) ? ctx.editor.readonly : ctx.editor.mode.get() === 'readonly'); + }); + }) + ); +}); diff --git a/src/test/ts/browser/EventBindingTest.ts b/src/test/ts/browser/EventBindingTest.ts index d54e0f34..5ae3f078 100644 --- a/src/test/ts/browser/EventBindingTest.ts +++ b/src/test/ts/browser/EventBindingTest.ts @@ -1,5 +1,4 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ -import { Assert, UnitTest } from '@ephox/bedrock-client'; +import { Assert, describe } from '@ephox/bedrock-client'; import { Arr, Obj, Fun } from '@ephox/katamari'; import { IAllProps } from 'src/main/ts/components/Editor'; import { configHandlers2 } from '../../../main/ts/Utils'; @@ -8,7 +7,7 @@ interface Handler { key: string; } -UnitTest.test('Event binding test', () => { +describe('EventBindingTest', () => { let calls: { type: 'on' | 'off'; name: string; handler: Handler }[]; let boundHandlers: Record; @@ -39,8 +38,8 @@ UnitTest.test('Event binding test', () => { const dummyLookupProp: any = (_key: K) => Fun.die('not implemented'); // dummy functions for handlers - const focusHandler = () => {}; - const blurHandler = () => {}; + const focusHandler = Fun.noop; + const blurHandler = Fun.noop; // check no handlers calls = []; @@ -80,5 +79,4 @@ UnitTest.test('Event binding test', () => { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument configHandlers2(dummyLookupProp, on, off, adapter, { onFocus: focusHandler, onBlur: blurHandler }, { onFocus: focusHandler }, boundHandlers); check({ Blur: 'off' }, [ 'onFocus' ]); - -}); \ No newline at end of file +}); diff --git a/src/test/ts/browser/LoadTinyTest.ts b/src/test/ts/browser/LoadTinyTest.ts index 61c81ef9..308168d7 100644 --- a/src/test/ts/browser/LoadTinyTest.ts +++ b/src/test/ts/browser/LoadTinyTest.ts @@ -1,87 +1,53 @@ -import { Chain, Log, Pipeline, Assertions } from '@ephox/agar'; -import { UnitTest } from '@ephox/bedrock-client'; -import { Arr, Strings, Global } from '@ephox/katamari'; -import { SelectorFilter, Attribute, SugarElement, Remove } from '@ephox/sugar'; - -import { ScriptLoader } from '../../../main/ts/ScriptLoader2'; -import { cRemove, cRender } from '../alien/Loader'; -import { VERSIONS, CLOUD_VERSIONS, type Version } from '../alien/TestHelpers'; - -const apiKey = 'qagffr3pkuv17a8on1afax661irst1hbr4e6tbv888sz91jc'; -UnitTest.asynctest('LoadTinyTest', (success, failure) => { - const cDeleteTinymce = Chain.op(() => { - ScriptLoader.reinitialize(); - - delete Global.tinymce; - delete Global.tinyMCE; - const hasTinymceUri = (attrName: string) => (elm: SugarElement) => - Attribute.getOpt(elm, attrName).exists((src) => Strings.contains(src, 'tinymce')); - - const elements = Arr.flatten([ - Arr.filter(SelectorFilter.all('script'), hasTinymceUri('src')), - Arr.filter(SelectorFilter.all('link'), hasTinymceUri('href')), - ]); - - Arr.each(elements, Remove.remove); +import { Assertions } from '@ephox/agar'; +import { describe, it } from '@ephox/bedrock-client'; +import { Global } from '@ephox/katamari'; + +import { CLOUD_VERSIONS, VALID_API_KEY, VERSIONS, type Version } from '../alien/TestHelpers'; +import { render } from '../alien/Loader'; + +const assertTinymceVersion = (version: Version) => { + Assertions.assertEq(`Loaded version of TinyMCE should be ${version}`, version, Global.tinymce.majorVersion); +}; + +describe('LoadTinyTest', () => { + VERSIONS.forEach((version) => { + it(`Should be able to load local version (${version}) of TinyMCE using the tinymceScriptSrc prop`, async () => { + using _ctx = await render({ tinymceScriptSrc: `/project/node_modules/tinymce-${version}/tinymce.min.js`, licenseKey: 'gpl' }); + assertTinymceVersion(version); + }); }); - const cAssertTinymceVersion = (version: Version) => Chain.op(() => { - Assertions.assertEq(`Loaded version of TinyMCE should be ${version}`, version, Global.tinymce.majorVersion); - }); - - Pipeline.async({}, [ - Log.chainsAsStep('Should be able to load local version of TinyMCE using the tinymceScriptSrc prop', '', [ - cDeleteTinymce, - - ...VERSIONS.flatMap((version) => [ - cRender({ tinymceScriptSrc: `/project/node_modules/tinymce-${version}/tinymce.min.js` }), - cAssertTinymceVersion(version), - cRemove, - cDeleteTinymce, - ]), - ]), - - Log.chainsAsStep('Should be able to load TinyMCE from Cloud', '', - CLOUD_VERSIONS.flatMap((version) => [ - cRender({ apiKey: 'a-fake-api-key', cloudChannel: version }), - cAssertTinymceVersion(version), - Chain.op(() => { - Assertions.assertEq( - 'TinyMCE should have been loaded from Cloud', - `https://cdn.tiny.cloud/1/a-fake-api-key/tinymce/${version}`, - Global.tinymce.baseURI.source - ); - }), - cRemove, - cDeleteTinymce, - ]) - ), - - Log.chainsAsStep('Should be able to load TinyMCE in hybrid', '', - CLOUD_VERSIONS.flatMap((version) => [ - cRender({ tinymceScriptSrc: [ + CLOUD_VERSIONS.forEach((version) => { + it(`Should be able to load TinyMCE from Cloud (${version})`, async () => { + const apiKey = 'a-fake-api-key'; + using _ctx = await render({ apiKey, cloudChannel: version }); + assertTinymceVersion(version); + Assertions.assertEq( + 'TinyMCE should have been loaded from Cloud', + `https://cdn.tiny.cloud/1/${apiKey}/tinymce/${version}`, + Global.tinymce.baseURI.source + ); + }); + + it(`Should be able to load TinyMCE (${version}) in hybrid`, async () => { + using _ctx = await render({ + tinymceScriptSrc: [ `/project/node_modules/tinymce-${version}/tinymce.min.js`, - `https://cdn.tiny.cloud/1/${apiKey}/tinymce/${version}/cloud-plugins.min.js?tinydrive=${version}` - ], plugins: [ 'tinydrive' ] }), - cAssertTinymceVersion(version), - Chain.op(() => { - Assertions.assertEq( - 'TinyMCE should have been loaded locally', - `/project/node_modules/tinymce-${version}`, - Global.tinymce.baseURI.path - ); - }), - Chain.op(() => { - Assertions.assertEq( - 'The tinydrive plugin should have defaults for the cloud', - `https://cdn.tiny.cloud/1/${apiKey}/tinymce-plugins/tinydrive/${version}/plugin.min.js`, - (Global.tinymce.defaultOptions || Global.tinymce.defaultSettings)?.custom_plugin_urls?.tinydrive - ); - }), - cRemove, - cDeleteTinymce, - Chain.wait(1000), - ]) - ) - ], success, failure); -}); \ No newline at end of file + `https://cdn.tiny.cloud/1/${VALID_API_KEY}/tinymce/${version}/cloud-plugins.min.js?tinydrive=${version}` + ], + plugins: [ 'tinydrive' ] + }); + assertTinymceVersion(version); + Assertions.assertEq( + 'TinyMCE should have been loaded locally', + `/project/node_modules/tinymce-${version}`, + Global.tinymce.baseURI.path + ); + Assertions.assertEq( + 'The tinydrive plugin should have defaults for the cloud', + `https://cdn.tiny.cloud/1/${VALID_API_KEY}/tinymce-plugins/tinydrive/${version}/plugin.min.js`, + (Global.tinymce.defaultOptions || Global.tinymce.defaultSettings)?.custom_plugin_urls?.tinydrive + ); + }); + }); +});