diff --git a/scripts/__tests__/ComponentTest/ComponentError.test.js b/scripts/__tests__/ComponentTest/ComponentError.test.js
new file mode 100755
index 00000000..7a833d04
--- /dev/null
+++ b/scripts/__tests__/ComponentTest/ComponentError.test.js
@@ -0,0 +1,43 @@
+import * as Horizon from '@cloudsop/horizon/index.ts';
+import { getLogUtils } from '../jest/testUtils';
+
+describe('Component Error Test', () => {
+ const LogUtils = getLogUtils();
+ it('createElement不能为null或undefined', () => {
+ const NullElement = null;
+ const UndefinedElement = undefined;
+
+ jest.spyOn(console, 'error').mockImplementation();
+ expect(() => {
+ Horizon.render(, document.createElement('div'));
+ }).toThrow('Component type is invalid, got: null');
+
+ expect(() => {
+ Horizon.render(, document.createElement('div'));
+ }).toThrow('Component type is invalid, got: undefined');
+
+ const App = () => {
+ return ;
+ };
+
+ let AppChild = () => {
+ return (
+
+ );
+ };
+
+ expect(() => {
+ Horizon.render(, document.createElement('div'));
+ }).toThrow('Component type is invalid, got: null');
+
+ AppChild = () => {
+ return (
+
+ );
+ };
+
+ expect(() => {
+ Horizon.render(, document.createElement('div'));
+ }).toThrow('Component type is invalid, got: undefined');
+ });
+});
\ No newline at end of file
diff --git a/scripts/__tests__/ComponentTest/Context.test.js b/scripts/__tests__/ComponentTest/Context.test.js
new file mode 100644
index 00000000..d64934ef
--- /dev/null
+++ b/scripts/__tests__/ComponentTest/Context.test.js
@@ -0,0 +1,361 @@
+import * as Horizon from '@cloudsop/horizon/index.ts';
+import { getLogUtils } from '../jest/testUtils';
+
+describe('Context Test', () => {
+ const LogUtils = getLogUtils();
+ it('Provider及其内部consumer组件都不受制于shouldComponentUpdate函数或者Horizon.memo()', () => {
+ const LanguageTypes = {
+ JAVA: 'Java',
+ JAVASCRIPT: 'JavaScript',
+ };
+ const defaultValue = { type: LanguageTypes.JAVASCRIPT };
+ const SystemLanguageContext = Horizon.createContext(defaultValue);
+ const SystemLanguageConsumer = SystemLanguageContext.Consumer;
+ const SystemLanguageProvider = (props) => {
+ LogUtils.log('SystemLanguageProvider');
+ return (
+
+ {props.children}
+
+ );
+ };
+
+ const Consumer = () => {
+ LogUtils.log('Consumer');
+ return (
+
+ {type => {
+ LogUtils.log('Consumer DOM mutations');
+ return {type}
;
+ }}
+
+ );
+ };
+
+ class Middle extends Horizon.Component {
+ shouldComponentUpdate() {
+ return false;
+ }
+ render() {
+ LogUtils.log('Middle');
+ return this.props.children;
+ }
+ }
+
+ const App = (props) => {
+ LogUtils.log('App');
+ return (
+
+
+
+
+
+
+
+ );
+ };
+
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('Java');
+ expect(LogUtils.getAndClear()).toEqual([
+ 'App',
+ 'SystemLanguageProvider',
+ 'Middle',
+ 'Middle',
+ 'Consumer',
+ 'Consumer DOM mutations'
+ ]);
+
+ // 组件不变,Middle没有更新,消费者也不会执行
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('Java');
+ expect(LogUtils.getAndClear()).toEqual([
+ 'App',
+ 'SystemLanguageProvider'
+ ]);
+
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('JavaScript');
+ // 组件更新,但是Middle没有更新,会绕过Middle
+ expect(LogUtils.getAndClear()).toEqual([
+ 'App',
+ 'SystemLanguageProvider',
+ 'Consumer DOM mutations'
+ ]);
+ });
+
+ it('嵌套consumer provider', () => {
+ const Num = {
+ ONE: 1,
+ TWO: 2,
+ };
+ const NumberContext = Horizon.createContext(0);
+ const NumberConsumer = NumberContext.Consumer;
+ const NumberProvider = (props) => {
+ LogUtils.log(`SystemLanguageProvider: ${props.type}`);
+ return (
+
+ {props.children}
+
+ );
+ };
+
+ const Consumer = () => {
+ LogUtils.log('Consumer');
+ return (
+
+ {type => {
+ LogUtils.log('Consumer DOM mutations');
+ return {type}
;
+ }}
+
+ );
+ };
+
+ class Middle extends Horizon.Component {
+ shouldComponentUpdate() {
+ return false;
+ }
+ render() {
+ LogUtils.log('Middle');
+ return this.props.children;
+ }
+ }
+
+ const App = (props) => {
+ LogUtils.log('App');
+ return (
+
+
+
+
+
+
+
+ );
+ };
+
+ // Consumer决定于距离它最近的provider
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('2');
+ expect(LogUtils.getAndClear()).toEqual([
+ 'App',
+ 'SystemLanguageProvider: 1',
+ 'SystemLanguageProvider: 2',
+ 'Middle',
+ 'Consumer',
+ 'Consumer DOM mutations'
+ ]);
+ // 更新
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('3');
+ expect(LogUtils.getAndClear()).toEqual([
+ 'App',
+ 'SystemLanguageProvider: 2',
+ 'SystemLanguageProvider: 3',
+ 'Consumer DOM mutations'
+ ]);
+ });
+
+ it('设置defaultValue', () => {
+ const Num = {
+ ONE: 1,
+ TWO: 2,
+ };
+ const NumberContext = Horizon.createContext(0);
+ const NewNumberContext = Horizon.createContext(1);
+ const NumberConsumer = NumberContext.Consumer;
+ const NumberProvider = props => {
+ return (
+
+ {props.children}
+
+ );
+ };
+ const NewNumberProvider = props => {
+ return (
+
+ {props.children}
+
+ );
+ };
+
+ class Middle extends Horizon.Component {
+ shouldComponentUpdate() {
+ return false;
+ }
+ render() {
+ return this.props.children;
+ }
+ }
+
+ const NewApp = (props) => {
+ return (
+
+
+
+ {type => {
+ LogUtils.log('Consumer DOM mutations');
+ return {type}
;
+ }}
+
+
+
+ );
+ };
+
+ const App = (props) => {
+ return (
+
+
+
+ {type => {
+ LogUtils.log('Consumer DOM mutations');
+ return {type}
;
+ }}
+
+
+
+ );
+ };
+
+ Horizon.render(, container);
+ // 没有匹配到Provider,会使用defaultValue
+ expect(container.querySelector('p').innerHTML).toBe('0');
+
+ // 更新,设置value为undefined
+ Horizon.render(, container);
+ // 设置value为undefined时,defaultValue不生效
+ expect(container.querySelector('p').innerHTML).toBe('');
+ });
+
+ it('不同provider下的多个consumer', () => {
+ const NumContext = Horizon.createContext(1);
+ const Consumer = NumContext.Consumer;
+
+ function Provider(props) {
+ return (
+
+ {value => (
+
+ {props.children}
+
+ )}
+
+ );
+ }
+
+ class Middle extends Horizon.Component {
+ shouldComponentUpdate() {
+ return false;
+ }
+ render() {
+ return this.props.children;
+ }
+ }
+
+ const App = props => {
+ return (
+
+
+
+
+
+ {value => {value}
}
+
+
+
+
+
+ {value => {value}
}
+
+
+
+
+ );
+ };
+
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('4');
+ expect(container.querySelector('#p').innerHTML).toBe('2');
+
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('6');
+ expect(container.querySelector('#p').innerHTML).toBe('3');
+ });
+
+ it('consumer里的child更新是不会重新渲染', () => {
+ const NumContext = Horizon.createContext(1);
+ const Consumer = NumContext.Consumer;
+
+ let setNum;
+ const ReturnDom = props => {
+ const [num, _setNum] = Horizon.useState(0);
+ setNum = _setNum;
+ LogUtils.log('ReturnDom');
+ return (
+
{`Context: ${props.context}, Num: ${num}`}
+ );
+ };
+
+ const App = props => {
+ return (
+
+
+ {value => {
+ LogUtils.log('Consumer');
+ return ;
+ }}
+
+
+ );
+ };
+
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('Context: 2, Num: 0');
+ expect(LogUtils.getAndClear()).toEqual([
+ 'Consumer',
+ 'ReturnDom'
+ ]);
+ setNum(3);
+ expect(container.querySelector('p').innerHTML).toBe('Context: 2, Num: 3');
+ expect(LogUtils.getAndClear()).toEqual(['ReturnDom']);
+ });
+
+
+ it('consumer可以拿到其他context的值', () => {
+ const NumContext = Horizon.createContext(1);
+ const TypeContext = Horizon.createContext('typeA');
+
+ const NumAndType = () => {
+ const type = Horizon.useContext(TypeContext);
+ return (
+
+ {value => {
+ LogUtils.log('Consumer');
+ return {`Num: ${value}, Type: ${type}`}
;
+ }}
+
+ );
+ };
+
+ const App = props => {
+ return (
+
+
+
+
+
+ );
+ };
+
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('Num: 2, Type: typeB');
+
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('Num: 2, Type: typeR');
+
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('Num: 8, Type: typeR');
+ });
+});
diff --git a/scripts/__tests__/ComponentTest/FragmentComponent.test.js b/scripts/__tests__/ComponentTest/FragmentComponent.test.js
new file mode 100755
index 00000000..eee9ceb4
--- /dev/null
+++ b/scripts/__tests__/ComponentTest/FragmentComponent.test.js
@@ -0,0 +1,474 @@
+import * as Horizon from '@cloudsop/horizon/index.ts';
+import { Text } from '../jest/commonComponents';
+import { getLogUtils } from '../jest/testUtils';
+
+describe('Fragment', () => {
+ const LogUtils = getLogUtils();
+ const {
+ useEffect,
+ useRef,
+ act,
+ } = Horizon;
+ it('可以渲染空元素', () => {
+ const element = (
+
+ );
+
+ Horizon.render(element, container);
+
+ expect(container.textContent).toBe('');
+ });
+ it('可以渲染单个元素', () => {
+ const element = (
+
+
+
+ );
+
+ Horizon.render(element, container);
+
+ expect(LogUtils.getAndClear()).toEqual(['Fragment']);
+ expect(container.textContent).toBe('Fragment');
+ });
+
+ it('可以渲染混合元素', () => {
+ const element = (
+
+ Java and
+
+ );
+
+ Horizon.render(element, container);
+
+ expect(LogUtils.getAndClear()).toEqual(['JavaScript']);
+ expect(container.textContent).toBe('Java and JavaScript');
+ });
+
+ it('可以渲染集合元素', () => {
+ const App = [, ];
+ const element = (
+ <>
+ {App}
+ >
+ );
+
+ Horizon.render(element, container);
+
+ expect(LogUtils.getAndClear()).toEqual(['Java', 'JavaScript']);
+ expect(container.textContent).toBe('JavaJavaScript');
+ });
+
+ it('元素被放进不同层级Fragment里时,状态不会保留', () => {
+ const ChildApp = (props) => {
+ const flag = useRef(true);
+ useEffect(() => {
+ if (flag.current) {
+ flag.current = false;
+ } else {
+ LogUtils.log('useEffect');
+ }
+ });
+
+ return {props.logo}
;
+ };
+
+ const App = (props) => {
+ return props.change ? (
+ <>
+
+ >
+
+ ) : (
+ <>
+ <>
+
+ >
+ >
+ );
+ };
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual([]);
+ act(() => {
+ Horizon.render(, container);
+ });
+ // 切换到不同层级Fragment时,副作用状态不会保留
+ expect(LogUtils.getNotClear()).toEqual([]);
+ expect(container.textContent).toBe('2');
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual([]);
+ expect(container.textContent).toBe('1');
+ });
+
+ it('元素被放进单层Fragment里,且在Fragment的顶部时,状态会保留', () => {
+ const ChildApp = (props) => {
+ const flag = useRef(true);
+ useEffect(() => {
+ if (flag.current) {
+ flag.current = false;
+ } else {
+ LogUtils.log('useEffect');
+ }
+ });
+
+ return {props.logo}
;
+ };
+
+ const App = (props) => {
+ return props.change ? (
+
+ ) : (
+ <>
+
+ >
+ );
+ };
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual([]);
+ act(() => {
+ Horizon.render(, container);
+ });
+ // 状态会保留
+ expect(LogUtils.getNotClear()).toEqual(['useEffect']);
+ expect(container.textContent).toBe('2');
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual(['useEffect', 'useEffect']);
+ expect(container.textContent).toBe('1');
+ });
+
+ it('元素被放进单层Fragment里,但不在Fragment的顶部时,状态不会保留', () => {
+ const ChildApp = (props) => {
+ const flag = useRef(true);
+ useEffect(() => {
+ if (flag.current) {
+ flag.current = false;
+ } else {
+ LogUtils.log('useEffect');
+ }
+ });
+
+ return {props.logo}
;
+ };
+
+ const App = (props) => {
+ return props.change ? (
+
+ ) : (
+ <>
+ 123
+
+ >
+ );
+ };
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual([]);
+ act(() => {
+ Horizon.render(, container);
+ });
+ // 状态不会保留
+ expect(LogUtils.getNotClear()).toEqual([]);
+ expect(container.textContent).toBe('1232');
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual([]);
+ expect(container.textContent).toBe('1');
+ });
+
+ it('元素被放进多层Fragment里时,状态不会保留', () => {
+ const ChildApp = (props) => {
+ const flag = useRef(true);
+ useEffect(() => {
+ if (flag.current) {
+ flag.current = false;
+ } else {
+ LogUtils.log('useEffect');
+ }
+ });
+
+ return {props.logo}
;
+ };
+
+ const App = (props) => {
+ return props.change ? (
+
+ ) : (
+ <>
+ <>
+ <>
+
+ >
+ >
+ >
+ );
+ };
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual([]);
+ act(() => {
+ Horizon.render(, container);
+ });
+ // 状态不会保留
+ expect(LogUtils.getNotClear()).toEqual([]);
+ expect(container.textContent).toBe('2');
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual([]);
+ expect(container.textContent).toBe('1');
+ });
+
+ it('元素被切换放进同级Fragment里时,状态会保留', () => {
+ const ChildApp = (props) => {
+ const flag = useRef(true);
+ useEffect(() => {
+ if (flag.current) {
+ flag.current = false;
+ } else {
+ LogUtils.log('useEffect');
+ }
+ });
+
+ return {props.logo}
;
+ };
+
+ const App = (props) => {
+ return props.change ? (
+ <>
+ <>
+ <>
+
+ >
+ >
+ >
+ ) : (
+ <>
+ <>
+ <>
+
+ >
+ >
+ >
+ );
+ };
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual([]);
+ act(() => {
+ Horizon.render(, container);
+ });
+ // 状态会保留
+ expect(LogUtils.getNotClear()).toEqual(['useEffect']);
+ expect(container.textContent).toBe('2');
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual(['useEffect', 'useEffect']);
+ expect(container.textContent).toBe('1');
+ });
+
+ it('元素被切换放进同级Fragment,且在数组顶层时,状态会保留', () => {
+ const ChildApp = (props) => {
+ const flag = useRef(true);
+ useEffect(() => {
+ if (flag.current) {
+ flag.current = false;
+ } else {
+ LogUtils.log('useEffect');
+ }
+ });
+
+ return {props.logo}
;
+ };
+
+ const App = (props) => {
+ return props.change ? (
+ <>
+ <>
+ <>
+
+ >
+ >
+ >
+ ) : (
+ <>
+ <>
+ <>
+ {[]}
+ >
+ >
+ >
+ );
+ };
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual([]);
+ act(() => {
+ Horizon.render(, container);
+ });
+ // 状态会保留
+ expect(LogUtils.getNotClear()).toEqual(['useEffect']);
+ expect(container.textContent).toBe('2');
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual(['useEffect', 'useEffect']);
+ expect(container.textContent).toBe('1');
+ });
+
+ it('数组里的顶层元素被切换放进单级Fragment时,状态会保留', () => {
+ const ChildApp = (props) => {
+ const flag = useRef(true);
+ useEffect(() => {
+ if (flag.current) {
+ flag.current = false;
+ } else {
+ LogUtils.log('useEffect');
+ }
+ });
+
+ return {props.logo}
;
+ };
+
+ const App = (props) => {
+ return props.change ? (
+ []
+ ) : (
+ <>
+
+ >
+ );
+ };
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual([]);
+ act(() => {
+ Horizon.render(, container);
+ });
+ // 状态会保留
+ expect(LogUtils.getNotClear()).toEqual(['useEffect']);
+ expect(container.textContent).toBe('2');
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual(['useEffect', 'useEffect']);
+ expect(container.textContent).toBe('1');
+ });
+
+ it('Fragment里的顶层数组里的顶层元素被切换放进不同级Fragment时,状态不会保留', () => {
+ const ChildApp = (props) => {
+ const flag = useRef(true);
+ useEffect(() => {
+ if (flag.current) {
+ flag.current = false;
+ } else {
+ LogUtils.log('useEffect');
+ }
+ });
+
+ return {props.logo}
;
+ };
+
+ const App = (props) => {
+ return props.change ? (
+ <>
+ []
+ >
+ ) : (
+ <>
+ <>
+
+ >
+ >
+ );
+ };
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual([]);
+ act(() => {
+ Horizon.render(, container);
+ });
+ // 状态会保留
+ expect(LogUtils.getNotClear()).toEqual([]);
+ expect(container.textContent).toBe('2');
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual([]);
+ expect(container.textContent).toBe('[1]');
+ });
+
+ it('Fragment的key值不同时,状态不会保留', () => {
+ const ChildApp = (props) => {
+ const flag = useRef(true);
+ useEffect(() => {
+ if (flag.current) {
+ flag.current = false;
+ } else {
+ LogUtils.log('useEffect');
+ }
+ });
+
+ return {props.logo}
;
+ };
+
+ const App = (props) => {
+ return props.change ? (
+
+
+
+ ) : (
+
+
+
+ );
+ };
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual([]);
+ act(() => {
+ Horizon.render(, container);
+ });
+ // 状态不会保留
+ expect(LogUtils.getNotClear()).toEqual([]);
+ expect(container.textContent).toBe('2');
+
+ act(() => {
+ Horizon.render(, container);
+ });
+ expect(LogUtils.getNotClear()).toEqual([]);
+ expect(container.textContent).toBe('1');
+ });
+});
diff --git a/scripts/__tests__/ComponentTest/FunctionComponent.test.js b/scripts/__tests__/ComponentTest/FunctionComponent.test.js
new file mode 100644
index 00000000..004dc15d
--- /dev/null
+++ b/scripts/__tests__/ComponentTest/FunctionComponent.test.js
@@ -0,0 +1,48 @@
+import * as Horizon from '@cloudsop/horizon/index.ts';
+describe('FunctionComponent Test', () => {
+ it('渲染无状态组件', () => {
+ const App = (props) => {
+ return {props.text}
;
+ };
+
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('app');
+ });
+
+ it('更新无状态组件', () => {
+ const App = (props) => {
+ return {props.text}
;
+ };
+
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('app');
+
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('ABC');
+
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('abc');
+ });
+
+ it('卸载无状态组件', () => {
+ const App = (props) => {
+ return {props.text}
;
+ };
+
+ Horizon.render(, container);
+ expect(container.querySelector('p').innerHTML).toBe('app');
+
+ Horizon.unmountComponentAtNode(container);
+ expect(container.querySelector('p')).toBe(null);
+ });
+
+ it('渲染空组件返回空子节点', () => {
+ const App = () => {
+ return ;
+ };
+
+ const realNode = Horizon.render(, container);
+ expect(realNode).toBe(null);
+ });
+
+});
\ No newline at end of file
diff --git a/scripts/__tests__/ComponentTest/HookTest/UseEffect.test.js b/scripts/__tests__/ComponentTest/HookTest/UseEffect.test.js
index 38ab9e71..129c2ec3 100644
--- a/scripts/__tests__/ComponentTest/HookTest/UseEffect.test.js
+++ b/scripts/__tests__/ComponentTest/HookTest/UseEffect.test.js
@@ -1,5 +1,5 @@
import * as Horizon from '@cloudsop/horizon/index.ts';
-import * as LogUtils from '../../jest/logUtils';
+import { getLogUtils } from '../../jest/testUtils';
import { Text } from '../../jest/commonComponents';
describe('useEffect Hook Test', () => {
@@ -12,6 +12,7 @@ describe('useEffect Hook Test', () => {
act,
} = Horizon;
+ const LogUtils = getLogUtils();
it('简单使用useEffect', () => {
const App = () => {
const [num, setNum] = useState(0);
diff --git a/scripts/__tests__/ComponentTest/HookTest/UseImperativeHandle.test.js b/scripts/__tests__/ComponentTest/HookTest/UseImperativeHandle.test.js
index 6924610c..6a21741a 100644
--- a/scripts/__tests__/ComponentTest/HookTest/UseImperativeHandle.test.js
+++ b/scripts/__tests__/ComponentTest/HookTest/UseImperativeHandle.test.js
@@ -1,6 +1,6 @@
import * as Horizon from '@cloudsop/horizon/index.ts';
-import * as LogUtils from '../../jest/logUtils';
import { Text } from '../../jest/commonComponents';
+import { getLogUtils } from '../../jest/testUtils';
describe('useImperativeHandle Hook Test', () => {
const {
@@ -10,7 +10,7 @@ describe('useImperativeHandle Hook Test', () => {
act,
} = Horizon;
const { unmountComponentAtNode } = Horizon;
-
+ const LogUtils = getLogUtils();
it('测试useImperativeHandle', () => {
let App = (props, ref) => {
@@ -48,7 +48,6 @@ describe('useImperativeHandle Hook Test', () => {
});
it('useImperativeHandle没有配置dep时自动更新', () => {
-
let App = (props, ref) => {
const [num, setNum] = useState(0);
useImperativeHandle(ref, () => ({ num, setNum }));
diff --git a/scripts/__tests__/ComponentTest/HookTest/UseLayoutEffect.test.js b/scripts/__tests__/ComponentTest/HookTest/UseLayoutEffect.test.js
index 4a087112..b0dd89d4 100644
--- a/scripts/__tests__/ComponentTest/HookTest/UseLayoutEffect.test.js
+++ b/scripts/__tests__/ComponentTest/HookTest/UseLayoutEffect.test.js
@@ -1,5 +1,5 @@
import * as Horizon from '@cloudsop/horizon/index.ts';
-import * as LogUtils from '../../jest/logUtils';
+import { getLogUtils } from '../../jest/testUtils';
import { Text } from '../../jest/commonComponents';
describe('useLayoutEffect Hook Test', () => {
@@ -9,7 +9,7 @@ describe('useLayoutEffect Hook Test', () => {
useLayoutEffect,
act,
} = Horizon;
-
+ const LogUtils = getLogUtils();
it('简单使用useLayoutEffect', () => {
const App = () => {
const [num, setNum] = useState(0);
diff --git a/scripts/__tests__/ComponentTest/HookTest/UseMemo.test.js b/scripts/__tests__/ComponentTest/HookTest/UseMemo.test.js
index 1170e6ee..24d381fe 100644
--- a/scripts/__tests__/ComponentTest/HookTest/UseMemo.test.js
+++ b/scripts/__tests__/ComponentTest/HookTest/UseMemo.test.js
@@ -1,9 +1,10 @@
import * as Horizon from '@cloudsop/horizon/index.ts';
-import * as LogUtils from '../../jest/logUtils';
+import { getLogUtils } from '../../jest/testUtils';
import { Text } from '../../jest/commonComponents';
describe('useMemo Hook Test', () => {
const { useMemo, useState } = Horizon;
+ const LogUtils = getLogUtils();
it('测试useMemo', () => {
let setMemo;
diff --git a/scripts/__tests__/ComponentTest/HookTest/UseRef.test.js b/scripts/__tests__/ComponentTest/HookTest/UseRef.test.js
index a873a0d4..b219e63b 100644
--- a/scripts/__tests__/ComponentTest/HookTest/UseRef.test.js
+++ b/scripts/__tests__/ComponentTest/HookTest/UseRef.test.js
@@ -1,9 +1,10 @@
import * as Horizon from '@cloudsop/horizon/index.ts';
-import * as LogUtils from '../../jest/logUtils';
+import { getLogUtils } from '../../jest/testUtils';
import { Text } from '../../jest/commonComponents';
describe('useRef Hook Test', () => {
const { useState, useRef } = Horizon;
+ const LogUtils = getLogUtils();
it('测试useRef', () => {
const App = () => {
diff --git a/scripts/__tests__/ComponentTest/HookTest/UseState.test.js b/scripts/__tests__/ComponentTest/HookTest/UseState.test.js
index 01db57c1..75aaf380 100644
--- a/scripts/__tests__/ComponentTest/HookTest/UseState.test.js
+++ b/scripts/__tests__/ComponentTest/HookTest/UseState.test.js
@@ -1,5 +1,5 @@
import * as Horizon from '@cloudsop/horizon/index.ts';
-import * as LogUtils from '../../jest/logUtils';
+import { getLogUtils } from '../../jest/testUtils';
import { Text } from '../../jest/commonComponents';
describe('useState Hook Test', () => {
@@ -10,6 +10,7 @@ describe('useState Hook Test', () => {
memo,
act,
} = Horizon;
+ const LogUtils = getLogUtils();
it('简单使用useState', () => {
const App = () => {
@@ -112,7 +113,7 @@ describe('useState Hook Test', () => {
it('useState与memo一起使用', () => {
let setNum;
- const App = memo((props) => {
+ const App = memo(() => {
const [num, _setNum] = useState(0);
setNum = _setNum;
return ;
diff --git a/scripts/__tests__/ComponentTest/LazyComponent.test.js b/scripts/__tests__/ComponentTest/LazyComponent.test.js
new file mode 100755
index 00000000..704dca45
--- /dev/null
+++ b/scripts/__tests__/ComponentTest/LazyComponent.test.js
@@ -0,0 +1,187 @@
+import * as Horizon from '@cloudsop/horizon/index.ts';
+import { Text } from '../jest/commonComponents';
+import { getLogUtils } from '../jest/testUtils';
+
+describe('LazyComponent Test', () => {
+ const LogUtils = getLogUtils();
+ const mockImport = jest.fn(async (component) => {
+ return { default: component };
+ });
+
+ it('Horizon.lazy()', async () => {
+ class LazyComponent extends Horizon.Component {
+ static defaultProps = { language: 'Java' };
+
+ render() {
+ const text = `${this.props.greeting}: ${this.props.language}`;
+ return {text};
+ }
+ }
+
+ const Lazy = Horizon.lazy(() => mockImport(LazyComponent));
+
+ Horizon.render(
+ }>
+
+ ,
+ container
+ );
+
+ expect(LogUtils.getAndClear()).toEqual(['Loading...']);
+ expect(container.textContent).toBe('Loading...');
+ expect(container.querySelector('span')).toBe(null);
+
+ await Promise.resolve();
+ Horizon.render(
+ }>
+
+ ,
+ container
+ );
+
+ expect(LogUtils.getAndClear()).toEqual([]);
+ expect(container.querySelector('span').innerHTML).toBe('Goodbye: Java');
+ });
+
+ it('同步解析', async () => {
+ const LazyApp = Horizon.lazy(() => ({
+ then(cb) {
+ cb({ default: Text });
+ },
+ }));
+
+ Horizon.render(
+ Loading...}>
+
+ ,
+ container
+ );
+
+ expect(LogUtils.getAndClear()).toEqual(['Lazy']);
+ expect(container.textContent).toBe('Lazy');
+ });
+
+ it('异常捕获边界', async () => {
+ class ErrorBoundary extends Horizon.Component {
+ state = {};
+ static getDerivedStateFromError(error) {
+ return { message: error.message };
+ }
+ render() {
+ return this.state.message
+ ? Error: {this.state.message}
+ : this.props.children;
+ }
+ }
+
+ const LazyComponent = () => {
+ const [num, setNum] = Horizon.useState(0);
+ if (num === 2) {
+ throw new Error('num is 2');
+ } else {
+ return (
+ <>
+ {num}
+