diff --git a/scripts/__tests__/EventTest/EventMain.test.js b/scripts/__tests__/EventTest/EventMain.test.js
new file mode 100644
index 00000000..1a3291c5
--- /dev/null
+++ b/scripts/__tests__/EventTest/EventMain.test.js
@@ -0,0 +1,131 @@
+import * as Horizon from '@cloudsop/horizon/index.ts';
+import * as LogUtils from '../jest/logUtils';
+import * as TestUtils from '../jest/testUtils';
+
+describe('事件', () => {
+ it('根节点挂载全量事件', () => {
+ const App = () => {
+ return
;
+ }
+ Horizon.render(, container);
+ console.log(TestUtils.getEventListeners(container));
+ })
+
+ it('事件捕获与冒泡', () => {
+ const App = () => {
+ return (
+ <>
+ LogUtils.log('div capture')} onClick={() => LogUtils.log('div bubble')}>
+
LogUtils.log('p capture')} onClick={() => LogUtils.log('p bubble')}>
+
+
+ >
+ );
+ }
+ Horizon.render(, container);
+ const a = container.querySelector('button');
+ a.click();
+ expect(LogUtils.getAndClear()).toEqual([
+ // 从外到内先捕获再冒泡
+ 'div capture',
+ 'p capture',
+ 'btn capture',
+ 'btn bubble',
+ 'p bubble',
+ 'div bubble'
+ ]);
+ })
+
+ it('returns 0', () => {
+ let keyCode = null;
+ const node = Horizon.render(
+ {
+ keyCode = e.keyCode;
+ }}
+ />,
+ container,
+ );
+ node.dispatchEvent(
+ new KeyboardEvent('keypress', {
+ keyCode: 65,
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+ expect(keyCode).toBe(65);
+ });
+
+ it('阻止事件冒泡', () => {
+ const App = () => {
+ return (
+ <>
+ LogUtils.log('div capture')} onClick={() => LogUtils.log('div bubble')}>
+
LogUtils.log('p capture')} onClick={() => LogUtils.log('p bubble')}>
+
+
+ >
+ );
+ }
+ Horizon.render(, container);
+ container.querySelector('button').click();
+
+ expect(LogUtils.getAndClear()).toEqual([
+ // 到button时停止冒泡
+ 'div capture',
+ 'p capture',
+ 'btn capture',
+ 'btn bubble'
+ ]);
+ })
+
+ it('阻止事件捕获', () => {
+ const App = () => {
+ return (
+ <>
+ TestUtils.stopBubbleOrCapture(e, 'div capture')} onClick={() => LogUtils.log('div bubble')}>
+
LogUtils.log('p capture')} onClick={() => LogUtils.log('p bubble')}>
+
+
+ >
+ );
+ }
+ Horizon.render(, container);
+ container.querySelector('button').click();
+
+ expect(LogUtils.getAndClear()).toEqual([
+ // 阻止捕获,不再继续向下执行
+ 'div capture'
+ ]);
+ })
+
+ it('阻止原生事件冒泡', () => {
+ const App = () => {
+ return (
+
+ );
+ }
+ Horizon.render(, container);
+ container.querySelector('div').addEventListener('click', () => {
+ LogUtils.log('div bubble');
+ }, false);
+ container.querySelector('p').addEventListener('click', () => {
+ LogUtils.log('p bubble');
+ }, false);
+ container.querySelector('button').addEventListener('click', (e) => {
+ LogUtils.log('btn bubble');
+ e.stopPropagation();
+ }, false);
+ container.querySelector('button').click();
+ expect(LogUtils.getAndClear()).toEqual([
+ 'btn bubble'
+ ]);
+ })
+})
diff --git a/scripts/__tests__/EventTest/FocusEvent.test.js b/scripts/__tests__/EventTest/FocusEvent.test.js
new file mode 100644
index 00000000..ee5e3c46
--- /dev/null
+++ b/scripts/__tests__/EventTest/FocusEvent.test.js
@@ -0,0 +1,46 @@
+import * as Horizon from '@cloudsop/horizon/index.ts';
+import * as LogUtils from '../jest/logUtils';
+import { act } from '../jest/customMatcher';
+
+describe('合成焦点事件', () => {
+
+ it('onFocus', () => {
+ const realNode = Horizon.render(
+ LogUtils.log(`onFocus: ${event.type}`)}
+ onFocusCapture={event => LogUtils.log(`onFocusCapture: ${event.type}`)}
+ />, container);
+
+ realNode.dispatchEvent(
+ new FocusEvent('focusin', {
+ bubbles: true,
+ cancelable: false,
+ }),
+ );
+
+ expect(LogUtils.getAndClear()).toEqual([
+ 'onFocusCapture: focus',
+ 'onFocus: focus',
+ ]);
+ });
+
+ it('onBlur', () => {
+ const realNode = Horizon.render(
+ LogUtils.log(`onBlur: ${event.type}`)}
+ onBlurCapture={event => LogUtils.log(`onBlurCapture: ${event.type}`)}
+ />, container);
+
+ realNode.dispatchEvent(
+ new FocusEvent('focusout', {
+ bubbles: true,
+ cancelable: false,
+ }),
+ );
+
+ expect(LogUtils.getAndClear()).toEqual([
+ 'onBlurCapture: blur',
+ 'onBlur: blur',
+ ]);
+ })
+})
\ No newline at end of file
diff --git a/scripts/__tests__/EventTest/KeyboardEvent.test.js b/scripts/__tests__/EventTest/KeyboardEvent.test.js
new file mode 100644
index 00000000..519cc9ce
--- /dev/null
+++ b/scripts/__tests__/EventTest/KeyboardEvent.test.js
@@ -0,0 +1,179 @@
+import * as Horizon from '@cloudsop/horizon/index.ts';
+import * as LogUtils from '../jest/logUtils';
+
+describe('Keyboard Event', () => {
+
+ it('keydown,keypress,keyup的keycode,charcode', () => {
+ const node = Horizon.render(
+ {
+ LogUtils.log('onKeyUp: keycode: ' + e.keyCode + ',charcode: ' + e.charCode);
+ }}
+ onKeyDown={(e) => {
+ LogUtils.log('onKeyDown: keycode: ' + e.keyCode + ',charcode: ' + e.charCode)
+ }}
+ />,
+ container,
+ );
+ node.dispatchEvent(
+ new KeyboardEvent('keydown', {
+ keyCode: 50,
+ code: 'Digit2',
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+ node.dispatchEvent(
+ new KeyboardEvent('keyup', {
+ keyCode: 50,
+ code: 'Digit2',
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+
+ expect(LogUtils.getAndClear()).toEqual([
+ 'onKeyDown: keycode: 50,charcode: 0',
+ 'onKeyUp: keycode: 50,charcode: 0'
+ ]);
+ });
+
+ it('keypress的keycode,charcode', () => {
+ const node = Horizon.render(
+ {
+ LogUtils.log('onKeyPress: keycode: ' + e.keyCode + ',charcode: ' + e.charCode);
+ }}
+ />,
+ container,
+ );
+ node.dispatchEvent(
+ new KeyboardEvent('keypress', {
+ charCode: 50,
+ code: 'Digit2',
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+
+ expect(LogUtils.getAndClear()).toEqual([
+ 'onKeyPress: keycode: 0,charcode: 50'
+ ]);
+ });
+
+ it('当charcode为13,且不设置keycode的时候', () => {
+ const node = Horizon.render(
+ {
+ LogUtils.log('onKeyPress: keycode: ' + e.keyCode + ',charcode: ' + e.charCode);
+ }}
+ />,
+ container,
+ );
+ node.dispatchEvent(
+ new KeyboardEvent('keypress', {
+ charCode: 13,
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+ expect(LogUtils.getAndClear()).toEqual([
+ 'onKeyPress: keycode: 0,charcode: 13'
+ ]);
+ });
+
+ it('keydown,keypress,keyup的code', () => {
+ const node = Horizon.render(
+ {
+ LogUtils.log('onKeyUp: code: ' + e.code);
+ }}
+ onKeyPress={(e) => {
+ LogUtils.log('onKeyPress: code: ' + e.code);
+ }}
+ onKeyDown={(e) => {
+ LogUtils.log('onKeyDown: code: ' + e.code);
+ }}
+ />,
+ container,
+ );
+ node.dispatchEvent(
+ new KeyboardEvent('keydown', {
+ code: 'Digit2',
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+
+ node.dispatchEvent(
+ new KeyboardEvent('keypress', {
+ keyCode: 50,
+ code: 'Digit2',
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+
+ node.dispatchEvent(
+ new KeyboardEvent('keyup', {
+ code: 'Digit2',
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+
+ expect(LogUtils.getAndClear()).toEqual([
+ 'onKeyDown: code: Digit2',
+ 'onKeyPress: code: Digit2',
+ 'onKeyUp: code: Digit2'
+ ]);
+ });
+
+ it('可以执行preventDefault和 stopPropagation', () => {
+ const keyboardProcessing = e => {
+ expect(e.isDefaultPrevented()).toBe(false);
+ e.preventDefault();
+ expect(e.isDefaultPrevented()).toBe(true);
+
+ expect(e.isPropagationStopped()).toBe(false);
+ e.stopPropagation();
+ expect(e.isPropagationStopped()).toBe(true);
+ LogUtils.log(e.type + ' handle');
+ };
+ const div = Horizon.render(
+ ,
+ container,
+ );
+
+ div.dispatchEvent(
+ new KeyboardEvent('keydown', {
+ keyCode: 40,
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+ div.dispatchEvent(
+ new KeyboardEvent('keyup', {
+ keyCode: 40,
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+ div.dispatchEvent(
+ new KeyboardEvent('keypress', {
+ charCode: 40,
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+ expect(LogUtils.getAndClear()).toEqual([
+ 'keydown handle',
+ 'keyup handle',
+ 'keypress handle'
+ ]);
+ });
+});
diff --git a/scripts/__tests__/EventTest/MouseEvent.test.js b/scripts/__tests__/EventTest/MouseEvent.test.js
new file mode 100644
index 00000000..e5ffbae1
--- /dev/null
+++ b/scripts/__tests__/EventTest/MouseEvent.test.js
@@ -0,0 +1,160 @@
+import * as Horizon from '@cloudsop/horizon/index.ts';
+import * as LogUtils from '../jest/logUtils';
+
+describe('MouseEvent Test', () => {
+ describe('onClick Test', () => {
+ it('绑定this', () => {
+ class App extends Horizon.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ num: this.props.num,
+ price: this.props.price
+ };
+ }
+
+ setNum() {
+ this.setState(
+ {
+ num: this.state.num + 1
+ }
+ )
+ }
+
+ setPrice = (e) => {
+ this.setState(
+ {
+ num: this.state.price + 1
+ }
+ )
+ }
+
+ render() {
+ return (
+ <>
+ {this.state.num}
+ {this.state.price}
+
+
+ >
+ );
+ }
+ }
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('0');
+ expect(container.querySelector('#p').innerHTML).toBe('100');
+ // 点击按钮触发num加1
+ container.querySelector('button').click();
+ expect(container.querySelector('p').innerHTML).toBe('1');
+
+ container.querySelector('#btn').click();
+ expect(container.querySelector('p').innerHTML).toBe('101');
+ });
+
+ it('点击触发', () => {
+ const handleClick = jest.fn();
+ Horizon.render(, container)
+ container.querySelector('button').click();
+ expect(handleClick).toHaveBeenCalledTimes(1);
+ for (let i = 0; i < 5; i++) {
+ container.querySelector('button').click();
+ }
+ expect(handleClick).toHaveBeenCalledTimes(6);
+ })
+ })
+
+ const test = (name, config) => {
+ const node = Horizon.render(config, container);
+ let event = new MouseEvent(name, {
+ relatedTarget: null,
+ bubbles: true,
+ screenX: 1
+ });
+ node.dispatchEvent(event);
+
+ expect(LogUtils.getAndClear()).toEqual([
+ `${name} capture`,
+ `${name} bubble`
+ ]);
+
+ event = new MouseEvent(name, {
+ relatedTarget: null,
+ bubbles: true,
+ screenX: 2
+ });
+ node.dispatchEvent(event);
+
+ // 再次触发新事件
+ expect(LogUtils.getAndClear()).toEqual([
+ `${name} capture`,
+ `${name} bubble`
+ ]);
+ }
+
+ describe('合成鼠标事件', () => {
+ it('onMouseMove', () => {
+ const onMouseMove = () => {
+ LogUtils.log('mousemove bubble');
+ };
+ const onMouseMoveCapture = () => {
+ LogUtils.log('mousemove capture');
+ };
+ test('mousemove', )
+ });
+
+ it('onMouseDown', () => {
+ const onMousedown = () => {
+ LogUtils.log('mousedown bubble');
+ };
+ const onMousedownCapture = () => {
+ LogUtils.log('mousedown capture');
+ };
+ test('mousedown', )
+ });
+
+ it('onMouseUp', () => {
+ const onMouseUp = () => {
+ LogUtils.log('mouseup bubble');
+ };
+ const onMouseUpCapture = () => {
+ LogUtils.log('mouseup capture');
+ };
+ test('mouseup', )
+ });
+
+ it('onMouseOut', () => {
+ const onMouseOut = () => {
+ LogUtils.log('mouseout bubble');
+ };
+ const onMouseOutCapture = () => {
+ LogUtils.log('mouseout capture');
+ };
+ test('mouseout', )
+ });
+
+ it('onMouseOver', () => {
+ const onMouseOver = () => {
+ LogUtils.log('mouseover bubble');
+ };
+ const onMouseOverCapture = () => {
+ LogUtils.log('mouseover capture');
+ };
+ test('mouseover', )
+ });
+ })
+})
diff --git a/scripts/__tests__/EventTest/WheelEvent.test.js b/scripts/__tests__/EventTest/WheelEvent.test.js
new file mode 100644
index 00000000..a0ebf933
--- /dev/null
+++ b/scripts/__tests__/EventTest/WheelEvent.test.js
@@ -0,0 +1,52 @@
+import * as Horizon from '@cloudsop/horizon/index.ts';
+import * as LogUtils from '../jest/logUtils';
+
+describe('合成滚轮事件', () => {
+ it('onWheel', () => {
+ const realNode = Horizon.render(
+ LogUtils.log(`onWheel: ${event.type}`)}
+ onWheelCapture={event => LogUtils.log(`onWheelCapture: ${event.type}`)}
+ />, container);
+
+ realNode.dispatchEvent(
+ new MouseEvent('wheel', {
+ bubbles: true,
+ cancelable: false,
+ }),
+ );
+
+ expect(LogUtils.getAndClear()).toEqual([
+ 'onWheelCapture: wheel',
+ 'onWheel: wheel'
+ ]);
+ });
+
+ it('可以执行preventDefault和 stopPropagation', () => {
+ const eventHandler = e => {
+ expect(e.isDefaultPrevented()).toBe(false);
+ e.preventDefault();
+ expect(e.isDefaultPrevented()).toBe(true);
+
+ expect(e.isPropagationStopped()).toBe(false);
+ e.stopPropagation();
+ expect(e.isPropagationStopped()).toBe(true);
+ LogUtils.log(e.type + ' handle');
+ };
+ const realNode = Horizon.render(
+
,
+ container
+ );
+
+ realNode.dispatchEvent(
+ new MouseEvent('wheel', {
+ bubbles: true,
+ cancelable: true,
+ }),
+ );
+ expect(LogUtils.getAndClear()).toEqual([
+ 'wheel handle'
+ ]);
+ });
+
+})
\ No newline at end of file
diff --git a/scripts/__tests__/jest/testUtils.js b/scripts/__tests__/jest/testUtils.js
new file mode 100644
index 00000000..8bab93d9
--- /dev/null
+++ b/scripts/__tests__/jest/testUtils.js
@@ -0,0 +1,26 @@
+import { allDelegatedNativeEvents } from '../../../libs/horizon/src/event/EventCollection';
+import * as LogUtils from './logUtils';
+
+export const stopBubbleOrCapture = (e, value) => {
+ LogUtils.log(value)
+ e.stopPropagation();
+};
+
+export const getEventListeners = (dom) => {
+ let ret = true
+ let keyArray = [];
+ for (var key in dom) {
+ keyArray.push(key);
+ }
+ try {
+ allDelegatedNativeEvents.forEach(event => {
+ if (!keyArray.includes(event)) {
+ ret = false;
+ throw new Error('没有挂载全量事件');
+ }
+ })
+ } catch (error) {
+
+ }
+ return ret;
+};
\ No newline at end of file