Skip to content

Commit 401a38d

Browse files
committed
TINY-3576: Switch to agar and bedrock for testing
1 parent 5490721 commit 401a38d

10 files changed

+2481
-781
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ lib
33
.DS_Store
44
yarn.lock
55
storybook-static
6-
*.tgz
6+
*.tgz
7+
scratch

package-lock.json

+2,247-667
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+6-20
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,13 @@
1616
"scripts": {
1717
"lint": "tslint 'src/**/*.ts?(x)'",
1818
"clean": "rimraf lib",
19-
"test": "jest",
19+
"test-manual": "bedrock -f src/**/*.test.tsx",
2020
"build": "npm run test && npm run clean && tsc -p ./tsconfig.es2015.json && tsc -p ./tsconfig.cjs.json",
2121
"watch": "tsc -w -p ./tsconfig.es2015.json",
2222
"storybook": "start-storybook -p 6006",
2323
"storybook:build": "build-storybook",
2424
"prepare": "npm run build"
2525
},
26-
"jest": {
27-
"transform": {
28-
"^.+\\.tsx?$": "ts-jest"
29-
},
30-
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
31-
"moduleFileExtensions": [
32-
"ts",
33-
"tsx",
34-
"js",
35-
"jsx",
36-
"json"
37-
],
38-
"setupFiles": [
39-
"<rootDir>/test/fakeGlobal.ts"
40-
]
41-
},
4226
"keywords": [],
4327
"author": "Ephox Inc",
4428
"license": "Apache-2.0",
@@ -50,6 +34,10 @@
5034
"react-dom": "^16.7.0"
5135
},
5236
"devDependencies": {
37+
"@ephox/agar": "^4.12.4",
38+
"@ephox/bedrock": "^4.2.8",
39+
"@ephox/mcagar": "^4.0.3",
40+
"@ephox/tslint-rules": "^1.0.7",
5341
"@storybook/addon-actions": "^4.1.11",
5442
"@storybook/addon-console": "^1.1.0",
5543
"@storybook/addon-info": "^4.1.11",
@@ -66,16 +54,14 @@
6654
"awesome-typescript-loader": "^5.2.1",
6755
"core-js": "^2.6.3",
6856
"jest": "^23.0.0",
69-
"prettier": "^1.16.2",
7057
"raf": "^3.4.1",
7158
"react": "^16.7.0",
7259
"react-dom": "^16.7.0",
7360
"rimraf": "^2.6.3",
61+
"tinymce": "^5.0.4",
7462
"ts-jest": "^23.10.5",
7563
"ts-loader": "^5.3.3",
7664
"tslint": "^5.12.1",
77-
"tslint-config-prettier": "^1.17.0",
78-
"tslint-plugin-prettier": "^2.0.1",
7965
"typescript": "^3.2.4",
8066
"webpack": "^4.29.0"
8167
}

src/components/Editor.test.tsx

-70
This file was deleted.

src/components/EditorInit.test.tsx

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { Assertions, Chain, GeneralSteps, Logger, NamedChain, Pipeline } from '@ephox/agar';
2+
import { UnitTest } from '@ephox/bedrock';
3+
import { Element } from '@ephox/dom-globals';
4+
import * as React from 'react';
5+
import { cRemove, cSetup, cNamedChainDirect } from './TestHelpers';
6+
7+
UnitTest.asynctest('Editor.test', (success, failure) => {
8+
const cAssertProperty = (propName: any, expected: any) => {
9+
return Chain.op((el: any) => {
10+
Assertions.assertEq(propName + ' should be ' + expected, el[propName], expected);
11+
});
12+
};
13+
14+
Pipeline.async({}, [
15+
Logger.t('tagName prop changes element', GeneralSteps.sequence([
16+
Logger.t('it is div by default for inline', Chain.asStep({}, [
17+
cSetup((Editor) => <Editor inline />),
18+
NamedChain.asChain([
19+
cNamedChainDirect('DOMNode'),
20+
NamedChain.read('DOMNode', cAssertProperty('tagName', 'DIV')),
21+
NamedChain.outputInput
22+
]),
23+
cRemove
24+
])),
25+
26+
Logger.t('can be set to inline in init', Chain.asStep({}, [
27+
cSetup((Editor) => <Editor init={{ inline: true }} />),
28+
NamedChain.asChain([
29+
cNamedChainDirect('DOMNode'),
30+
NamedChain.read('DOMNode', cAssertProperty('tagName', 'DIV')),
31+
NamedChain.outputInput
32+
]),
33+
cRemove
34+
])),
35+
36+
Logger.t('it can be changed to p', Chain.asStep({}, [
37+
cSetup((Editor) => <Editor inline tagName='p' />),
38+
NamedChain.asChain([
39+
cNamedChainDirect('DOMNode'),
40+
NamedChain.read('DOMNode', cAssertProperty('tagName', 'P')),
41+
NamedChain.outputInput
42+
]),
43+
cRemove
44+
])),
45+
46+
Logger.t('iframe editor does not change element', Chain.asStep({}, [
47+
cSetup((Editor) => <Editor tagName='p' />),
48+
NamedChain.asChain([
49+
cNamedChainDirect('DOMNode'),
50+
NamedChain.read('DOMNode', cAssertProperty('tagName', 'TEXTAREA')),
51+
NamedChain.outputInput
52+
]),
53+
cRemove
54+
]))
55+
])),
56+
57+
Logger.t('id is set automatically if id prop not provided', GeneralSteps.sequence([
58+
Logger.t('is set normally if prop is provided', Chain.asStep({}, [
59+
cSetup((Editor) => <Editor id='test' />),
60+
NamedChain.asChain([
61+
cNamedChainDirect('DOMNode'),
62+
NamedChain.read('DOMNode', cAssertProperty('id', 'test')),
63+
NamedChain.outputInput
64+
]),
65+
cRemove
66+
])),
67+
68+
Logger.t('gets set automatically to uuid if not set', Chain.asStep({}, [
69+
cSetup((Editor) => <Editor />),
70+
NamedChain.asChain([
71+
cNamedChainDirect('DOMNode'),
72+
NamedChain.read('DOMNode', Chain.op((node: Element) => {
73+
Assertions.assertEq('Should not be falsy', !!node.id, true);
74+
})),
75+
NamedChain.outputInput
76+
]),
77+
cRemove
78+
])),
79+
])),
80+
81+
Logger.t('sets name on form', GeneralSteps.sequence([
82+
Logger.t('is not set when prop is not provided', Chain.asStep({}, [
83+
cSetup((Editor) => <Editor />),
84+
NamedChain.asChain([
85+
cNamedChainDirect('DOMNode'),
86+
NamedChain.read('DOMNode', cAssertProperty('name', '')),
87+
NamedChain.outputInput
88+
]),
89+
cRemove
90+
])),
91+
92+
Logger.t('is set when prop is provided', Chain.asStep({}, [
93+
cSetup((Editor) => <Editor textareaName='test' />),
94+
NamedChain.asChain([
95+
cNamedChainDirect('DOMNode'),
96+
NamedChain.read('DOMNode', cAssertProperty('name', 'test')),
97+
NamedChain.outputInput
98+
]),
99+
cRemove
100+
])),
101+
])),
102+
], success, failure);
103+
});

src/components/TestHelpers.tsx

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { Chain, NamedChain } from '@ephox/agar';
2+
import { Fun, Option } from '@ephox/katamari';
3+
import * as React from 'react';
4+
import * as ReactDOM from 'react-dom';
5+
import { getTinymce } from 'src/TinyMCE';
6+
import { Editor, IAllProps } from './Editor';
7+
import 'tinymce/tinymce';
8+
9+
export interface Payload {
10+
DOMNode: Element;
11+
editor: any;
12+
ref: React.RefObject<Editor>;
13+
root: HTMLElement;
14+
}
15+
16+
type OnEditorLoaded = (editor: any, ref: React.RefObject<Editor>) => void;
17+
18+
type TestEditor = (props: IAllProps) => JSX.Element;
19+
20+
const setTinymceBaseUrl = (baseUrl: string) => {
21+
const tinymce = getTinymce();
22+
const prefix = document.location.protocol + '//' + document.location.host;
23+
tinymce.baseURL = baseUrl.indexOf('://') === -1 ? prefix + baseUrl : baseUrl;
24+
tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL);
25+
};
26+
27+
const getTestEditor = (onLoaded: OnEditorLoaded): TestEditor => {
28+
return (props: IAllProps): JSX.Element => {
29+
const originalInit = props.init || {};
30+
const originalSetup = originalInit.setup || Fun.noop;
31+
const ref: React.RefObject<Editor> = React.createRef();
32+
33+
const init: Record<string, any> = {
34+
...originalInit,
35+
setup: (editor: any) => {
36+
originalSetup(editor);
37+
38+
editor.on('SkinLoaded', () => {
39+
setTimeout(() => {
40+
onLoaded(editor, ref);
41+
}, 0);
42+
});
43+
}
44+
};
45+
46+
setTinymceBaseUrl(init.base_url || `/project/node_modules/tinymce`);
47+
return <Editor ref={ref} {...props} init={init} />;
48+
};
49+
};
50+
51+
const cSetup = (createElement: (Ed: TestEditor) => JSX.Element) => {
52+
return Chain.async<Payload, Payload>((_, next, die) => {
53+
const root = document.createElement('div');
54+
document.body.append(root);
55+
56+
const onEditorLoaded: OnEditorLoaded = (editor, ref) => {
57+
Option.from(ref.current)
58+
.map(ReactDOM.findDOMNode)
59+
.filter((val) => val instanceof Element)
60+
.fold(() => die('Could not find DOMNode'), (DOMNode) => {
61+
next({
62+
ref,
63+
root,
64+
editor,
65+
DOMNode: DOMNode as Element
66+
});
67+
});
68+
};
69+
70+
const testEditor = getTestEditor(onEditorLoaded);
71+
const editorElement = createElement(testEditor);
72+
ReactDOM.render(editorElement, root);
73+
});
74+
};
75+
76+
const cRemove = Chain.op((res: Payload) => {
77+
ReactDOM.unmountComponentAtNode(res.root);
78+
});
79+
80+
const cNamedChainDirect = (name: keyof Payload) => NamedChain.direct(
81+
NamedChain.inputName(),
82+
Chain.mapper((res: Payload) => res[name]),
83+
name
84+
);
85+
86+
const EventState = () => {
87+
const state: Record<string, any> = {};
88+
89+
const handler = (name: string) => {
90+
return (...args: any[]) => {
91+
state[name] = args;
92+
};
93+
};
94+
95+
const get = (name: string) => {
96+
return state[name];
97+
};
98+
99+
return {
100+
handler,
101+
get
102+
};
103+
};
104+
105+
export {
106+
cSetup,
107+
cRemove,
108+
cNamedChainDirect,
109+
EventState
110+
};

0 commit comments

Comments
 (0)