Skip to content

Commit

Permalink
INT-3297: Refactored tests to use newer style BDD testing
Browse files Browse the repository at this point in the history
  • Loading branch information
danoaky-tiny committed Apr 10, 2024
1 parent 9d35308 commit a675164
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 248 deletions.
125 changes: 58 additions & 67 deletions src/test/ts/browser/EditorBehaviorTest.ts
Original file line number Diff line number Diff line change
@@ -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<Events.EditorEventMap['SetContent']>;

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/;
Expand All @@ -29,106 +29,97 @@ 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<SetContentEvent>('onSetContent', (events) => {
eventStore.each<SetContentEvent>('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',
versionRegex.test(version) ? '<p><br data-mce-bogus="1"></p>' : '',
events[0].editorEvent.content
);
Assertions.assertEq('Second arg should be editor', true, isEditor(events[0].editor));
}),

eventStore.cClearState,

cEditor(cSetContent('<p>Initial Content</p>')),

});
eventStore.clearState();

ctx.editor.setContent('<p>Initial Content</p>');
// tinymce native event
eventStore.cEach<SetContentEvent>('onSetContent', (events) => {
eventStore.each<SetContentEvent>('onSetContent', (events) => {
Assertions.assertEq('onSetContent should have been fired once', 1, events.length);
Assertions.assertEq('First arg should be event from Tiny', '<p>Initial Content</p>', events[0].editorEvent.content);
Assertions.assertEq('Second arg should be editor', true, isEditor(events[0].editor));
}),
});

// tinymce-react unique event
eventStore.cEach<string>('onEditorChange', (events) => {
eventStore.each<string>('onEditorChange', (events) => {
Assertions.assertEq('First arg should be new content', '<p>Initial Content</p>', 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('<p>Initial Content</p>')),
cEditor(cSetContent('<p>Initial Content</p>')), // Repeat
ctx.editor.setContent('<p>Initial Content</p>');
ctx.editor.setContent('<p>Initial Content</p>'); // 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: '<p>Initial Content</p>' }),
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: '<p>Initial Content</p>' });
await ctx.reRender({ onSetContent: eventStore.createHandler('onSetContent') });

cEditor(cAssertContent('<p>Initial Content</p>')),
cEditor(cSetContent('<p>New Content</p>')),
TinyAssertions.assertContent(ctx.editor, '<p>Initial Content</p>');
ctx.editor.setContent('<p>New Content</p>');

eventStore.cEach<SetContentEvent>('onSetContent', (events) => {
eventStore.each<SetContentEvent>('onSetContent', (events) => {
Assertions.assertEq('Should have bound handler, hence new content', '<p>New Content</p>', 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<SetContentEvent>('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<SetContentEvent>('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) ? '<p><br data-mce-bogus="1"></p>' : '',
events[0].editorEvent.content);
}),
eventStore.cClearState,
cEditor(cSetContent('<p>Initial Content</p>')),
});
eventStore.clearState();
ctx.editor.setContent('<p>Initial Content</p>');

cReRender({ onSetContent: eventStore.createHandler('NewHandler') }),
cEditor(cSetContent('<p>New Content</p>')),
await ctx.reRender({ onSetContent: eventStore.createHandler('NewHandler') }),
ctx.editor.setContent('<p>New Content</p>');

eventStore.cEach<SetContentEvent>('InitialHandler', (events) => {
eventStore.each<SetContentEvent>('InitialHandler', (events) => {
Assertions.assertEq('Initial handler should have been unbound, hence initial content', '<p>Initial Content</p>', events[0].editorEvent.content);
}),
eventStore.cEach<SetContentEvent>('NewHandler', (events) => {
eventStore.each<SetContentEvent>('NewHandler', (events) => {
Assertions.assertEq('New handler should have been bound, hence new content', '<p>New Content</p>', events[0].editorEvent.content);
}),
})

eventStore.cClearState,
cRemove
])),
])
eventStore.clearState();
});
})
);

Pipeline.async({}, VERSIONS.map(sTestVersion), success, failure);
});
});
159 changes: 69 additions & 90 deletions src/test/ts/browser/EditorInitTest.ts
Original file line number Diff line number Diff line change
@@ -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<string, unknown>)[propName]);
});
function assertProperty<T extends HTMLElement | Record<string, unknown>, K extends keyof T>(obj: T, propName: K, expected: T[K]): void
function assertProperty(obj: {}, propName: string, expected: unknown): void;
function assertProperty(obj: Record<string, unknown>, 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: '<p>Initial Value</p>' }),
cEditor(cAssertContent('<p>Initial Value</p>')),
cReRender({ value: '<p>New Value</p>' }),
cEditor(cAssertContent('<p>New Value</p>')),
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: '<p>Initial Value</p>' });
TinyAssertions.assertContent(ctx.editor, '<p>Initial Value</p>');
ctx.reRender({ ...defaultProps, value: '<p>New Value</p>' });
TinyAssertions.assertContent(ctx.editor, '<p>New Value</p>');
});

Pipeline.async({}, VERSIONS.map(sTestVersion), success, failure);
});
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');
});
})
);
});
12 changes: 5 additions & 7 deletions src/test/ts/browser/EventBindingTest.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<string, Handler>;

Expand Down Expand Up @@ -39,8 +38,8 @@ UnitTest.test('Event binding test', () => {
const dummyLookupProp: any = <K extends keyof IAllProps>(_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 = [];
Expand Down Expand Up @@ -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' ]);

});
});
Loading

0 comments on commit a675164

Please sign in to comment.