forked from Github/ocrcc-chatbox
covering all javascript
This commit is contained in:
parent
afc2709742
commit
e5c61ce998
@ -1,4 +1,10 @@
|
|||||||
import Enzyme from 'enzyme';
|
import Enzyme from 'enzyme';
|
||||||
import Adapter from 'enzyme-adapter-react-16';
|
import Adapter from 'enzyme-adapter-react-16';
|
||||||
|
|
||||||
Enzyme.configure({ adapter: new Adapter() });
|
Enzyme.configure({ adapter: new Adapter() });
|
||||||
|
|
||||||
|
Object.defineProperty(document, 'readyState', {
|
||||||
|
val: 'complete',
|
||||||
|
get() { return this.val; },
|
||||||
|
set(s) { this.val = s; },
|
||||||
|
});
|
||||||
|
1961
package-lock.json
generated
1961
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@ -6,7 +6,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack",
|
"build": "webpack",
|
||||||
"start": "webpack-serve --config ./webpack.config.js --mode development --open",
|
"start": "webpack-serve --config ./webpack.config.js --mode development --open",
|
||||||
"test": "jest"
|
"test": "jest",
|
||||||
|
"test-update-snapshots": "jest --updateSnapshot"
|
||||||
},
|
},
|
||||||
"babel": {
|
"babel": {
|
||||||
"presets": [
|
"presets": [
|
||||||
@ -17,6 +18,13 @@
|
|||||||
"jest": {
|
"jest": {
|
||||||
"coverageDirectory": "./coverage/",
|
"coverageDirectory": "./coverage/",
|
||||||
"collectCoverage": true,
|
"collectCoverage": true,
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"<rootDir>/src/**/*.js?(x)"
|
||||||
|
],
|
||||||
|
"coveragePathIgnorePatterns": [
|
||||||
|
"/node_modules/",
|
||||||
|
"/test-helpers/"
|
||||||
|
],
|
||||||
"transform": {
|
"transform": {
|
||||||
"^.+\\.(js|jsx|mjs)$": "<rootDir>/node_modules/babel-jest",
|
"^.+\\.(js|jsx|mjs)$": "<rootDir>/node_modules/babel-jest",
|
||||||
"^.+\\.(css|scss)$": "<rootDir>/jest/cssTransform.js",
|
"^.+\\.(css|scss)$": "<rootDir>/jest/cssTransform.js",
|
||||||
@ -24,10 +32,16 @@
|
|||||||
},
|
},
|
||||||
"setupFiles": [
|
"setupFiles": [
|
||||||
"<rootDir>/jest/setup.js"
|
"<rootDir>/jest/setup.js"
|
||||||
|
],
|
||||||
|
"snapshotSerializers": [
|
||||||
|
"enzyme-to-json/serializer"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"serve": {
|
"serve": {
|
||||||
"content": ["./dist", "./public"]
|
"content": [
|
||||||
|
"./dist",
|
||||||
|
"./public"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"author": "seriousben https://github.com/seriousben",
|
"author": "seriousben https://github.com/seriousben",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -41,6 +55,7 @@
|
|||||||
"css-loader": "^0.28.11",
|
"css-loader": "^0.28.11",
|
||||||
"enzyme": "^3.3.0",
|
"enzyme": "^3.3.0",
|
||||||
"enzyme-adapter-react-16": "^1.1.1",
|
"enzyme-adapter-react-16": "^1.1.1",
|
||||||
|
"enzyme-to-json": "^3.3.4",
|
||||||
"eslint": "^4.19.1",
|
"eslint": "^4.19.1",
|
||||||
"eslint-config-airbnb": "^16.1.0",
|
"eslint-config-airbnb": "^16.1.0",
|
||||||
"eslint-import-resolver-webpack": "^0.10.0",
|
"eslint-import-resolver-webpack": "^0.10.0",
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<script src="/embeddable-widget.js"></script>
|
<script src="/embeddable-widget.js"></script>
|
||||||
<script>
|
<script>
|
||||||
EmbeddableWidget.render();
|
EmbeddableWidget.mount();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1 +0,0 @@
|
|||||||
58305
|
|
175
src/__snapshots__/widget.test.js.snap
Normal file
175
src/__snapshots__/widget.test.js.snap
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`<Widget /> open/close 1`] = `
|
||||||
|
<Widget>
|
||||||
|
<div
|
||||||
|
className="docked-widget"
|
||||||
|
>
|
||||||
|
<Transition
|
||||||
|
appear={false}
|
||||||
|
enter={true}
|
||||||
|
exit={true}
|
||||||
|
in={false}
|
||||||
|
mountOnEnter={false}
|
||||||
|
onEnter={[Function]}
|
||||||
|
onEntered={[Function]}
|
||||||
|
onEntering={[Function]}
|
||||||
|
onExit={[Function]}
|
||||||
|
onExited={[Function]}
|
||||||
|
onExiting={[Function]}
|
||||||
|
timeout={250}
|
||||||
|
unmountOnExit={false}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="widget widget-exited"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="widget-header"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="widget-header-title"
|
||||||
|
>
|
||||||
|
Header
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
className="widget-header-icon"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="widget-body"
|
||||||
|
>
|
||||||
|
Body
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="widget-footer"
|
||||||
|
>
|
||||||
|
Footer
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
<a
|
||||||
|
className="dock"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
^ OPEN ^
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</Widget>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`<Widget /> open/close 2`] = `
|
||||||
|
<Widget>
|
||||||
|
<div
|
||||||
|
className="docked-widget"
|
||||||
|
>
|
||||||
|
<Transition
|
||||||
|
appear={false}
|
||||||
|
enter={true}
|
||||||
|
exit={true}
|
||||||
|
in={true}
|
||||||
|
mountOnEnter={false}
|
||||||
|
onEnter={[Function]}
|
||||||
|
onEntered={[Function]}
|
||||||
|
onEntering={[Function]}
|
||||||
|
onExit={[Function]}
|
||||||
|
onExited={[Function]}
|
||||||
|
onExiting={[Function]}
|
||||||
|
timeout={250}
|
||||||
|
unmountOnExit={false}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="widget widget-entering"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="widget-header"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="widget-header-title"
|
||||||
|
>
|
||||||
|
Header
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
className="widget-header-icon"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="widget-body"
|
||||||
|
>
|
||||||
|
Body
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="widget-footer"
|
||||||
|
>
|
||||||
|
Footer
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</Widget>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`<Widget /> open/close 3`] = `
|
||||||
|
<Widget>
|
||||||
|
<div
|
||||||
|
className="docked-widget"
|
||||||
|
>
|
||||||
|
<Transition
|
||||||
|
appear={false}
|
||||||
|
enter={true}
|
||||||
|
exit={true}
|
||||||
|
in={false}
|
||||||
|
mountOnEnter={false}
|
||||||
|
onEnter={[Function]}
|
||||||
|
onEntered={[Function]}
|
||||||
|
onEntering={[Function]}
|
||||||
|
onExit={[Function]}
|
||||||
|
onExited={[Function]}
|
||||||
|
onExiting={[Function]}
|
||||||
|
timeout={250}
|
||||||
|
unmountOnExit={false}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="widget widget-exited"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="widget-header"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="widget-header-title"
|
||||||
|
>
|
||||||
|
Header
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
className="widget-header-icon"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="widget-body"
|
||||||
|
>
|
||||||
|
Body
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="widget-footer"
|
||||||
|
>
|
||||||
|
Footer
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
<a
|
||||||
|
className="dock"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
^ OPEN ^
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</Widget>
|
||||||
|
`;
|
23
src/index.js
23
src/index.js
@ -3,16 +3,22 @@ import ReactDOM from 'react-dom';
|
|||||||
import Widget from './widget';
|
import Widget from './widget';
|
||||||
|
|
||||||
export default class EmbeddableWidget {
|
export default class EmbeddableWidget {
|
||||||
static render() {
|
static el;
|
||||||
|
|
||||||
|
static mount() {
|
||||||
const component = <Widget />;
|
const component = <Widget />;
|
||||||
|
|
||||||
function doRender() {
|
function doRender() {
|
||||||
const containerEl = document.createElement('div');
|
if (EmbeddableWidget.el) {
|
||||||
document.body.appendChild(containerEl);
|
throw new Error('EmbeddableWidget is already mounted, unmount first');
|
||||||
|
}
|
||||||
|
const el = document.createElement('div');
|
||||||
|
document.body.appendChild(el);
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
component,
|
component,
|
||||||
containerEl,
|
el,
|
||||||
);
|
);
|
||||||
|
EmbeddableWidget.el = el;
|
||||||
}
|
}
|
||||||
if (document.readyState === 'complete') {
|
if (document.readyState === 'complete') {
|
||||||
doRender();
|
doRender();
|
||||||
@ -22,4 +28,13 @@ export default class EmbeddableWidget {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unmount() {
|
||||||
|
if (!EmbeddableWidget.el) {
|
||||||
|
throw new Error('EmbeddableWidget is not mounted, mount first');
|
||||||
|
}
|
||||||
|
ReactDOM.unmountComponentAtNode(EmbeddableWidget.el);
|
||||||
|
EmbeddableWidget.el.parentNode.removeChild(EmbeddableWidget.el);
|
||||||
|
EmbeddableWidget.el = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
44
src/index.test.js
Normal file
44
src/index.test.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import EmbeddableWidget from './index';
|
||||||
|
import { waitForSelection } from './test-helpers';
|
||||||
|
|
||||||
|
describe('EmbeddableWidget', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
document.readyState = 'complete';
|
||||||
|
if (EmbeddableWidget.el) {
|
||||||
|
EmbeddableWidget.unmount();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('#mount document becomes ready', async () => {
|
||||||
|
document.readyState = 'loading';
|
||||||
|
EmbeddableWidget.mount();
|
||||||
|
window.dispatchEvent(new Event('load', {}));
|
||||||
|
await waitForSelection(document, 'div');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('#mount document complete', async () => {
|
||||||
|
EmbeddableWidget.mount();
|
||||||
|
await waitForSelection(document, 'div');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('#mount twice', async () => {
|
||||||
|
EmbeddableWidget.mount();
|
||||||
|
expect(() => EmbeddableWidget.mount()).toThrow('already mounted');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('#unmount', async () => {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
document.body.appendChild(el);
|
||||||
|
expect(document.querySelectorAll('div')).toHaveLength(1);
|
||||||
|
|
||||||
|
EmbeddableWidget.el = el;
|
||||||
|
EmbeddableWidget.unmount();
|
||||||
|
|
||||||
|
expect(document.querySelectorAll('div')).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('#unmount without mounting', async () => {
|
||||||
|
expect(() => EmbeddableWidget.unmount()).toThrow('not mounted');
|
||||||
|
});
|
||||||
|
});
|
30
src/test-helpers/index.js
Normal file
30
src/test-helpers/index.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
function checkFunc(dom, selector) {
|
||||||
|
if (typeof dom.update === 'function') {
|
||||||
|
const el = dom.update().find(selector);
|
||||||
|
if (el.exists()) {
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const els = dom.querySelectorAll(selector);
|
||||||
|
if (els.length !== 0) {
|
||||||
|
return els;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function waitForSelection(dom, selector) {
|
||||||
|
let numSleep = 0;
|
||||||
|
for (;;) {
|
||||||
|
const el = checkFunc(dom, selector);
|
||||||
|
if (el) {
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
if (numSleep > 2) {
|
||||||
|
throw new Error(`could not find ${selector}`);
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 250));
|
||||||
|
numSleep += 1;
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,37 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
import { mount } from 'enzyme';
|
||||||
import Widget from './widget';
|
import Widget from './widget';
|
||||||
|
import { waitForSelection } from './test-helpers';
|
||||||
|
|
||||||
|
describe('<Widget />', () => {
|
||||||
|
test('open/close', async () => {
|
||||||
|
const widgetDom = mount(<Widget />);
|
||||||
|
expect(widgetDom).toMatchSnapshot();
|
||||||
|
|
||||||
|
{
|
||||||
|
const dockAnchorEl = widgetDom.find('a.dock');
|
||||||
|
expect(dockAnchorEl).toHaveLength(1);
|
||||||
|
// open widget
|
||||||
|
dockAnchorEl.simulate('click');
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(widgetDom).toMatchSnapshot();
|
||||||
|
|
||||||
|
// dock does not exist anymore
|
||||||
|
expect(widgetDom.find('a.dock')).toHaveLength(0);
|
||||||
|
|
||||||
|
const closeAnchorEl = await waitForSelection(widgetDom, 'a.widget-header-icon');
|
||||||
|
|
||||||
|
expect(closeAnchorEl).toHaveLength(1);
|
||||||
|
// close widget
|
||||||
|
closeAnchorEl.simulate('click');
|
||||||
|
|
||||||
|
{
|
||||||
|
const dockAnchorEl = await waitForSelection(widgetDom, 'a.dock');
|
||||||
|
expect(dockAnchorEl).toHaveLength(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(widgetDom).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
test('simple test', () => {
|
|
||||||
const widgetDom = shallow(<Widget />);
|
|
||||||
console.log(widgetDom.html());
|
|
||||||
expect(widgetDom.find('.docked-widget').exists()).toBe(true);
|
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user