Match-id-8848190da18e3de604e59e794b073dbad5cdc07d

This commit is contained in:
* 2022-04-24 14:17:10 +08:00 committed by *
commit 4cbccebe13
22 changed files with 1118 additions and 55 deletions

View File

@ -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 (
<SystemLanguageContext.Provider value={props.type}>
{props.children}
</SystemLanguageContext.Provider>
);
};
const Consumer = () => {
LogUtils.log('Consumer');
return (
<SystemLanguageConsumer>
{type => {
LogUtils.log('Consumer DOM mutations');
return <p>{type}</p>;
}}
</SystemLanguageConsumer>
);
};
class Middle extends Horizon.Component {
shouldComponentUpdate() {
return false;
}
render() {
LogUtils.log('Middle');
return this.props.children;
}
}
const App = (props) => {
LogUtils.log('App');
return (
<SystemLanguageProvider type={props.value}>
<Middle>
<Middle>
<Consumer />
</Middle>
</Middle>
</SystemLanguageProvider>
);
};
Horizon.render(<App value={LanguageTypes.JAVA} />, container);
expect(container.querySelector('p').innerHTML).toBe('Java');
expect(LogUtils.getAndClear()).toEqual([
'App',
'SystemLanguageProvider',
'Middle',
'Middle',
'Consumer',
'Consumer DOM mutations'
]);
// 组件不变Middle没有更新消费者也不会执行
Horizon.render(<App value={LanguageTypes.JAVA} />, container);
expect(container.querySelector('p').innerHTML).toBe('Java');
expect(LogUtils.getAndClear()).toEqual([
'App',
'SystemLanguageProvider'
]);
Horizon.render(<App value={LanguageTypes.JAVASCRIPT} />, 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 (
<NumberContext.Provider value={props.type}>
{props.children}
</NumberContext.Provider>
);
};
const Consumer = () => {
LogUtils.log('Consumer');
return (
<NumberConsumer>
{type => {
LogUtils.log('Consumer DOM mutations');
return <p>{type}</p>;
}}
</NumberConsumer>
);
};
class Middle extends Horizon.Component {
shouldComponentUpdate() {
return false;
}
render() {
LogUtils.log('Middle');
return this.props.children;
}
}
const App = (props) => {
LogUtils.log('App');
return (
<NumberProvider type={props.value}>
<NumberProvider type={props.value + 1}>
<Middle>
<Consumer />
</Middle>
</NumberProvider>
</NumberProvider>
);
};
// Consumer决定于距离它最近的provider
Horizon.render(<App value={Num.ONE} />, container);
expect(container.querySelector('p').innerHTML).toBe('2');
expect(LogUtils.getAndClear()).toEqual([
'App',
'SystemLanguageProvider: 1',
'SystemLanguageProvider: 2',
'Middle',
'Consumer',
'Consumer DOM mutations'
]);
// 更新
Horizon.render(<App value={Num.TWO} />, 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 (
<NumberContext.Provider value={props.type}>
{props.children}
</NumberContext.Provider>
);
};
const NewNumberProvider = props => {
return (
<NewNumberContext.Provider value={props.type}>
{props.children}
</NewNumberContext.Provider>
);
};
class Middle extends Horizon.Component {
shouldComponentUpdate() {
return false;
}
render() {
return this.props.children;
}
}
const NewApp = (props) => {
return (
<NewNumberProvider value={props.value}>
<Middle>
<NumberConsumer>
{type => {
LogUtils.log('Consumer DOM mutations');
return <p>{type}</p>;
}}
</NumberConsumer>
</Middle>
</NewNumberProvider>
);
};
const App = (props) => {
return (
<NumberProvider value={props.value}>
<Middle>
<NumberConsumer>
{type => {
LogUtils.log('Consumer DOM mutations');
return <p>{type}</p>;
}}
</NumberConsumer>
</Middle>
</NumberProvider>
);
};
Horizon.render(<NewApp value={Num.ONE} />, container);
// 没有匹配到Provider,会使用defaultValue
expect(container.querySelector('p').innerHTML).toBe('0');
// 更新,设置value为undefined
Horizon.render(<App value={undefined} />, 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 (
<Consumer>
{value => (
<NumContext.Provider value={props.value || value * 2}>
{props.children}
</NumContext.Provider>
)}
</Consumer>
);
}
class Middle extends Horizon.Component {
shouldComponentUpdate() {
return false;
}
render() {
return this.props.children;
}
}
const App = props => {
return (
<Provider value={props.value}>
<Middle>
<Middle>
<Provider>
<Consumer>
{value => <p>{value}</p>}
</Consumer>
</Provider>
</Middle>
<Middle>
<Consumer>
{value => <p id='p'>{value}</p>}
</Consumer>
</Middle>
</Middle>
</Provider>
);
};
Horizon.render(<App value={2} />, container);
expect(container.querySelector('p').innerHTML).toBe('4');
expect(container.querySelector('#p').innerHTML).toBe('2');
Horizon.render(<App value={3} />, 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 (
<p>{`Context: ${props.context}, Num: ${num}`}</p>
);
};
const App = props => {
return (
<NumContext.Provider value={props.value}>
<Consumer>
{value => {
LogUtils.log('Consumer');
return <ReturnDom context={value} />;
}}
</Consumer>
</NumContext.Provider>
);
};
Horizon.render(<App value={2} />, 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 (
<NumContext.Consumer>
{value => {
LogUtils.log('Consumer');
return <p>{`Num: ${value}, Type: ${type}`}</p>;
}}
</NumContext.Consumer>
);
};
const App = props => {
return (
<NumContext.Provider value={props.num}>
<TypeContext.Provider value={props.type}>
<NumAndType />
</TypeContext.Provider>
</NumContext.Provider>
);
};
Horizon.render(<App num={2} type={'typeB'} />, container);
expect(container.querySelector('p').innerHTML).toBe('Num: 2, Type: typeB');
Horizon.render(<App num={2} type={'typeR'} />, container);
expect(container.querySelector('p').innerHTML).toBe('Num: 2, Type: typeR');
Horizon.render(<App num={8} type={'typeR'} />, container);
expect(container.querySelector('p').innerHTML).toBe('Num: 8, Type: typeR');
});
});

View File

@ -0,0 +1,48 @@
import * as Horizon from '@cloudsop/horizon/index.ts';
describe('FunctionComponent Test', () => {
it('渲染无状态组件', () => {
const App = (props) => {
return <p>{props.text}</p>;
};
Horizon.render(<App text='app' />, container);
expect(container.querySelector('p').innerHTML).toBe('app');
});
it('更新无状态组件', () => {
const App = (props) => {
return <p>{props.text}</p>;
};
Horizon.render(<App text='app' />, container);
expect(container.querySelector('p').innerHTML).toBe('app');
Horizon.render(<App text='ABC' />, container);
expect(container.querySelector('p').innerHTML).toBe('ABC');
Horizon.render(<App text='abc' />, container);
expect(container.querySelector('p').innerHTML).toBe('abc');
});
it('卸载无状态组件', () => {
const App = (props) => {
return <p>{props.text}</p>;
};
Horizon.render(<App text='app' />, container);
expect(container.querySelector('p').innerHTML).toBe('app');
Horizon.unmountComponentAtNode(container);
expect(container.querySelector('p')).toBe(null);
});
it('渲染空组件返回空子节点', () => {
const App = () => {
return <div />;
};
const realNode = Horizon.render(<App />, container);
expect(realNode).toBe(null);
});
});

View File

@ -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);

View File

@ -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 }));

View File

@ -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);

View File

@ -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;

View File

@ -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 = () => {

View File

@ -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 <Text text={num} />;

View File

@ -0,0 +1,475 @@
import * as Horizon from '@cloudsop/horizon/index.ts';
import { getLogUtils } from '../jest/testUtils';
describe('LifeCycle Test', () => {
const LogUtils = getLogUtils();
describe('LifeCycle function', () => {
it('不能在componentWillMount里setState', () => {
class App extends Horizon.Component {
state = {};
UNSAFE_componentWillMount() {
this.setState = {
num: 1
};
}
render() {
return <p>{this.state.num}</p>;
}
}
const realNode = Horizon.render(<App />, container);
// 不能在componentWillMount里setState
expect(realNode.textContent).toBe(undefined);
});
it('componentDidMount里调用setState()将触发额外渲染', () => {
class ChildApp extends Horizon.Component {
constructor(props) {
super(props);
}
componentDidMount() {
LogUtils.log(this.props.isShow);
}
render() {
return <p>{this.props.isShow}</p>;
}
}
class App extends Horizon.Component {
constructor(props) {
super(props);
LogUtils.log('constructor');
this.state = { shouldShowChild: false };
}
componentDidMount() {
LogUtils.log('componentDidMount');
this.setState({ shouldShowChild: true });
}
render() {
return (
<div>
{this.state.shouldShowChild ? <ChildApp isShow={this.state.shouldShowChild} /> : <div />}
</div>
);
}
}
const realNode = Horizon.render(<App />, container);
// 确实触发了额外渲染
expect(LogUtils.getAndClear()).toEqual([
'constructor',
'componentDidMount',
true
]);
// 可以在 componentDidMount() 里直接调用 setState()。它将触发额外渲染,但此渲染会发生在浏览器更新屏幕之前
expect(container.querySelector('p').innerHTML).toBe('');
// 在 componentDidMount() 里可以更新state
expect(realNode.state).toStrictEqual({ 'shouldShowChild': true });
});
it('调用 this.setState() 通常不会触发 UNSAFE_componentWillReceiveProps()', () => {
class App extends Horizon.Component {
state = {};
update = () => {
this.setState({ num: 4 });
}
UNSAFE_componentWillReceiveProps() {
LogUtils.log('componentWillReceiveProps');
this.setState = {
num: 1
};
}
render() {
return <p>{this.state.num}</p>;
}
}
const realNode = Horizon.render(<App />, container);
expect(realNode.textContent).toBe(undefined);
realNode.update();
expect(LogUtils.getAndClear()).toEqual([]);
});
it('不能在componentWillReceiveProps里setState', () => {
class ChildApp extends Horizon.Component {
state = {};
UNSAFE_componentWillReceiveProps() {
this.state = { text: 'text' };
}
render() {
LogUtils.log(this.state.text);
return <div>{this.state.text}</div>;
}
}
class App extends Horizon.Component {
state = {};
update = () => {
this.setState({ num: 4 });
}
render() {
return <ChildApp num={this.state.num} />;
}
}
const realNode = Horizon.render(<App />, container);
expect(realNode.textContent).toBe(undefined);
realNode.update();
expect(LogUtils.getAndClear()).toEqual([
undefined,
'text',
]);
// 不能在componentWillMount里setState
expect(realNode.textContent).toBe(undefined);
});
it('shouldComponentUpdate与getDerivedStateFromProps', () => {
class App extends Horizon.Component {
constructor(props) {
super(props);
this.state = {
num: props.num,
};
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.num === prevState.num) {
return null;
}
return { num: nextProps.num };
}
shouldComponentUpdate(nextProps, nextState) {
LogUtils.log('shouldComponentUpdate');
return nextState.num !== this.state.num;
}
render() {
return <p>{this.state.num}</p>;
}
}
Horizon.render(<App num={1} />, container);
// 初次渲染不会调用shouldComponentUpdate
expect(LogUtils.getAndClear()).toEqual([]);
expect(container.querySelector('p').innerHTML).toBe('1');
Horizon.render(<App num={1} />, container);
// getDerivedStateFromProps判断state没有变化时会调用shouldComponentUpdate
expect(LogUtils.getAndClear()).toEqual(['shouldComponentUpdate']);
expect(container.querySelector('p').innerHTML).toBe('1');
Horizon.render(<App num={2} />, container);
// getDerivedStateFromProps判断state变化时会调用shouldComponentUpdate
expect(LogUtils.getAndClear()).toEqual(['shouldComponentUpdate']);
expect(container.querySelector('p').innerHTML).toBe('2');
});
it('如果shouldComponentUpdate()返回值为false,则不会调用componentDidUpdate()', () => {
class App extends Horizon.Component {
constructor(props) {
super(props);
this.state = {
num: props.num,
};
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.num === prevState.num) {
return null;
}
return { num: nextProps.num };
}
shouldComponentUpdate(nextProps, nextState) {
return nextState.num !== this.state.num;
}
componentDidUpdate() {
LogUtils.log('componentDidUpdate');
}
render() {
return <p>{this.state.num}</p>;
}
}
Horizon.render(<App num={1} />, container);
expect(container.querySelector('p').innerHTML).toBe('1');
Horizon.render(<App num={1} />, container);
// 不会调用componentDidUpdate()
expect(LogUtils.getAndClear()).toEqual([]);
expect(container.querySelector('p').innerHTML).toBe('1');
Horizon.render(<App num={2} />, container);
// 调用componentDidUpdate()
expect(LogUtils.getAndClear()).toEqual(['componentDidUpdate']);
expect(container.querySelector('p').innerHTML).toBe('2');
});
it('getSnapshotBeforeUpdate()的返回值会作为componentDidUpdate()的第三个参数', () => {
class App extends Horizon.Component {
constructor(props) {
super(props);
this.state = {
num: 0,
};
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.num === prevState.num) {
return null;
}
return { num: nextProps.num };
}
getSnapshotBeforeUpdate(prevProps, prevState) {
LogUtils.log(
`getSnapshotBeforeUpdate prevProps:${prevProps.num} prevState:${prevState.num}`,
);
return 'Snapshot';
}
componentDidUpdate(prevProps, prevState, snapshot) {
LogUtils.log(
`componentDidUpdate prevProps:${prevProps.num} prevState:${prevState.num} snapshot:${snapshot}`,
);
}
render() {
return <p>{this.state.num}</p>;
}
}
Horizon.render(<App />, container);
expect(LogUtils.getAndClear()).toEqual([]);
expect(container.querySelector('p').innerHTML).toBe('');
Horizon.render(<App num={1} />, container);
// Snapshot作为componentDidUpdate()的第三个参数
expect(LogUtils.getAndClear()).toEqual([
'getSnapshotBeforeUpdate prevProps:undefined prevState:undefined',
'componentDidUpdate prevProps:undefined prevState:undefined snapshot:Snapshot',
]);
expect(container.querySelector('p').innerHTML).toBe('1');
Horizon.render(<App num={1} />, container);
expect(LogUtils.getAndClear()).toEqual([
'getSnapshotBeforeUpdate prevProps:1 prevState:1',
'componentDidUpdate prevProps:1 prevState:1 snapshot:Snapshot',
]);
expect(container.querySelector('p').innerHTML).toBe('1');
Horizon.render(<App num={2} />, container);
expect(LogUtils.getAndClear()).toEqual([
'getSnapshotBeforeUpdate prevProps:1 prevState:1',
'componentDidUpdate prevProps:1 prevState:1 snapshot:Snapshot',
]);
expect(container.querySelector('p').innerHTML).toBe('2');
});
it('无论什么原因触发了渲染,只要有渲染就会触发getDerivedStateFromProps', () => {
class App extends Horizon.Component {
constructor(props) {
super(props);
this.state = {
num: 0,
};
}
static getDerivedStateFromProps(nextProps, prevState) {
LogUtils.log(
`getDerivedStateFromProps nextProps:${nextProps.num} prevState:${prevState.num}`,
);
}
render() {
return <p>{this.state.num}</p>;
}
}
let realNode = Horizon.render(<App />, container);
realNode = Horizon.render(<App num={1} />, container);
realNode.forceUpdate();
// 触发了3次渲染
expect(LogUtils.getAndClear()).toEqual([
'getDerivedStateFromProps nextProps:undefined prevState:0',
'getDerivedStateFromProps nextProps:1 prevState:0',
'getDerivedStateFromProps nextProps:1 prevState:0',
]);
});
});
it('生命周期执行顺序', () => {
class ChildApp extends Horizon.Component {
UNSAFE_componentWillMount() {
LogUtils.log('Child componentWillMount');
}
componentDidMount() {
LogUtils.log('Child componentDidMount');
}
UNSAFE_componentWillReceiveProps() {
LogUtils.log('Child componentWillReceiveProps');
}
shouldComponentUpdate(nextProps, nextState) {
LogUtils.log('Child shouldComponentUpdates');
return this.props.number !== nextProps.number;
}
UNSAFE_componentWillUpdate() {
LogUtils.log('Child componentWillUpdate');
}
componentDidUpdate() {
LogUtils.log('Child componentDidUpdate');
}
componentWillUnmount() {
LogUtils.log('Child componentWillUnmount');
}
render() {
return <p>{this.props.number}</p>;
}
}
class App extends Horizon.Component {
UNSAFE_componentWillMount() {
LogUtils.log('componentWillMount');
}
componentDidMount() {
LogUtils.log('componentDidMount');
}
UNSAFE_componentWillReceiveProps() {
LogUtils.log('componentWillReceiveProps');
}
shouldComponentUpdate(nextProps, nextState) {
LogUtils.log('shouldComponentUpdates');
return this.props.num !== nextProps.num;
}
UNSAFE_componentWillUpdate() {
LogUtils.log('componentWillUpdate');
}
componentDidUpdate() {
LogUtils.log('componentDidUpdate');
}
componentWillUnmount() {
LogUtils.log('componentWillUnmount');
}
render() {
return <ChildApp number={this.props.num} />;
}
}
Horizon.render(<App num={1} />, container);
expect(container.textContent).toBe('1');
expect(LogUtils.getAndClear()).toEqual([
'componentWillMount',
'Child componentWillMount',
'Child componentDidMount',
'componentDidMount'
]);
Horizon.render(<App num={2} />, container);
expect(container.textContent).toBe('2');
expect(LogUtils.getAndClear()).toEqual([
'componentWillReceiveProps',
'shouldComponentUpdates',
'componentWillUpdate',
'Child componentWillReceiveProps',
'Child shouldComponentUpdates',
'Child componentWillUpdate',
'Child componentDidUpdate',
'componentDidUpdate'
]);
Horizon.unmountComponentAtNode(container);
expect(container.textContent).toBe('');
expect(LogUtils.getAndClear()).toEqual([
'componentWillUnmount',
'Child componentWillUnmount'
]);
});
it('新生命周期执行顺序', () => {
class ChildApp extends Horizon.Component {
static getDerivedStateFromProps(props, state) {
LogUtils.log('Child getDerivedStateFromProps');
}
componentDidMount() {
LogUtils.log('Child componentDidMount');
}
shouldComponentUpdate(nextProps, nextState) {
LogUtils.log('Child shouldComponentUpdates');
return this.props.number !== nextProps.number;
}
componentDidUpdate() {
LogUtils.log('Child componentDidUpdate');
}
getSnapshotBeforeUpdate() {
LogUtils.log('Child getSnapshotBeforeUpdate');
}
componentWillUnmount() {
LogUtils.log('Child componentWillUnmount');
}
render() {
return <p>{this.props.number}</p>;
}
}
class App extends Horizon.Component {
static getDerivedStateFromProps(props, state) {
LogUtils.log('getDerivedStateFromProps');
}
componentDidMount() {
LogUtils.log('componentDidMount');
}
shouldComponentUpdate(nextProps, nextState) {
LogUtils.log('shouldComponentUpdates');
return this.props.num !== nextProps.num;
}
getSnapshotBeforeUpdate() {
LogUtils.log('getSnapshotBeforeUpdate');
}
componentDidUpdate() {
LogUtils.log('componentDidUpdate');
}
componentWillUnmount() {
LogUtils.log('componentWillUnmount');
}
render() {
return <ChildApp number={this.props.num} />;
}
}
Horizon.render(<App num={1} />, container);
expect(container.textContent).toBe('1');
expect(LogUtils.getAndClear()).toEqual([
'getDerivedStateFromProps',
'Child getDerivedStateFromProps',
'Child componentDidMount',
'componentDidMount'
]);
Horizon.render(<App num={2} />, container);
expect(container.textContent).toBe('2');
expect(LogUtils.getAndClear()).toEqual([
'getDerivedStateFromProps',
'shouldComponentUpdates',
'Child getDerivedStateFromProps',
'Child shouldComponentUpdates',
'Child getSnapshotBeforeUpdate',
'getSnapshotBeforeUpdate',
'Child componentDidUpdate',
'componentDidUpdate'
]);
Horizon.unmountComponentAtNode(container);
expect(container.textContent).toBe('');
expect(LogUtils.getAndClear()).toEqual([
'componentWillUnmount',
'Child componentWillUnmount'
]);
});
});

View File

@ -1,9 +1,10 @@
/* eslint-disable @typescript-eslint/no-empty-function */
import * as Horizon from '@cloudsop/horizon/index.ts';
import * as LogUtils from '../jest/logUtils';
import { getLogUtils } from '../jest/testUtils';
describe('Dom Input', () => {
const { act } = Horizon;
const LogUtils = getLogUtils();
describe('type checkbox', () => {
it('没有设置checked属性时控制台不会报错', () => {

View File

@ -130,7 +130,7 @@ describe('Dom Select', () => {
expect(realNode.options[1].selected).toBe(true);
});
it('设置defaultValue后select不受控', () => {
it('设置defaultValue后,select不受控', () => {
const selectNode = (
<select defaultValue={'Vue'}>
<option value='React'>React.js</option>

View File

@ -0,0 +1,150 @@
import * as Horizon from '@cloudsop/horizon/index.ts';
describe('Dom Textarea', () => {
it('设置value', () => {
let realNode = Horizon.render(<textarea value='text' />, container);
expect(realNode.getAttribute('value')).toBe(null);
expect(realNode.value).toBe('text');
realNode = Horizon.render(<textarea value={0} />, container);
expect(realNode.getAttribute('value')).toBe(null);
expect(realNode.value).toBe('0');
realNode = Horizon.render(<textarea value={true} />, container);
expect(realNode.getAttribute('value')).toBe(null);
expect(realNode.value).toBe('true');
realNode = Horizon.render(<textarea value={false} />, container);
expect(realNode.getAttribute('value')).toBe(null);
expect(realNode.value).toBe('false');
});
it('设置value为对象', () => {
let textareaValue = {
toString: () => {
return 'Vue';
}
};
const textareaNode = (
<textarea value={textareaValue} />
);
const realNode = Horizon.render(textareaNode, container);
expect(realNode.value).toBe('Vue');
textareaValue = {
toString: () => {
return 'React';
}
};
const newTextareaNode = <textarea value={textareaValue} />;
// 改变value会影响select的状态
Horizon.render(newTextareaNode, container);
expect(realNode.value).toBe('React');
});
it('受控组件value不变', () => {
let realNode = Horizon.render(<textarea value='text' />, container);
expect(realNode.getAttribute('value')).toBe(null);
expect(realNode.value).toBe('text');
// 先修改
Object.getOwnPropertyDescriptor(
HTMLTextAreaElement.prototype,
'value',
).set.call(realNode, 'textabc');
// 再触发事件
container.querySelector('textarea').dispatchEvent(
new Event('change', {
bubbles: true,
cancelable: true,
}),
);
// 组件受控想要改变value需要通过onChange改变state
expect(realNode.value).toBe('text');
});
it('设置defaultValue', () => {
let defaultVal = 'Vue';
const textareaNode = <textarea defaultValue={defaultVal} />;
let realNode = Horizon.render(textareaNode, container);
expect(realNode.value).toBe('Vue');
defaultVal = 'React';
// 改变defaultValue没有影响
realNode = Horizon.render(textareaNode, container);
expect(realNode.value).toBe('Vue');
Horizon.unmountComponentAtNode(container);
defaultVal = 0;
realNode = Horizon.render(<textarea defaultValue={defaultVal} />, container);
expect(realNode.value).toBe('0');
Horizon.unmountComponentAtNode(container);
defaultVal = true;
realNode = Horizon.render(<textarea defaultValue={defaultVal} />, container);
expect(realNode.value).toBe('true');
Horizon.unmountComponentAtNode(container);
defaultVal = false;
realNode = Horizon.render(<textarea defaultValue={defaultVal} />, container);
expect(realNode.value).toBe('false');
Horizon.render(<textarea>123</textarea>, container);
expect(realNode.value).toBe('false');
});
it('设置defaultValue为对象', () => {
let textareaValue = {
toString: () => {
return 'Vue';
}
};
const textareaNode = (
<textarea defaultValue={textareaValue} />
);
const realNode = Horizon.render(textareaNode, container);
expect(realNode.value).toBe('Vue');
});
it('设置defaultValue后,select不受控', () => {
const textareaNode = <textarea defaultValue={'text'} />;
let realNode = Horizon.render(textareaNode, container);
expect(realNode.value).toBe('text');
// 先修改
Object.getOwnPropertyDescriptor(
HTMLTextAreaElement.prototype,
'value',
).set.call(realNode, 'ABC');
// 再触发事件
container.querySelector('textarea').dispatchEvent(
new Event('change', {
bubbles: true,
cancelable: true,
}),
);
// 鼠标改变textarea生效
Horizon.render(textareaNode, container);
expect(realNode.value).toBe('ABC');
});
it('受控与非受控切换', () => {
// 非受控切换为受控
let realNode = Horizon.render(<textarea defaultValue='text' />, container);
expect(realNode.value).toBe('text');
Horizon.render(<textarea value='newtext' onChange={() => { }} />, container);
expect(realNode.value).toBe('newtext');
Horizon.unmountComponentAtNode(container);
// 受控切换为非受控
realNode = Horizon.render(<textarea value='text' onChange={() => { }} />, container);
expect(realNode.value).toBe('text');
Horizon.render(<textarea defaultValue='newtext' onChange={() => { }} />, container);
expect(realNode.value).toBe('text');
});
it('textarea的孩子', () => {
let realNode = Horizon.render(<textarea>{1234}</textarea>, container);
expect(realNode.value).toBe('1234');
realNode = Horizon.render(<textarea>{5678}</textarea>, container);
// realNode.value依旧为1234
expect(realNode.value).toBe('1234');
});
});

View File

@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Dom Textarea should not incur unnecessary DOM mutations 1`] = `<textarea />`;
exports[`Dom Textarea should not incur unnecessary DOM mutations 2`] = `<textarea />`;

View File

@ -1,8 +1,8 @@
import * as Horizon from '@cloudsop/horizon/index.ts';
import * as LogUtils from '../jest/logUtils';
import * as TestUtils from '../jest/testUtils';
describe('事件', () => {
const LogUtils = TestUtils.getLogUtils();
it('根节点挂载全量事件', () => {
const App = () => {
return <div />;

View File

@ -1,7 +1,8 @@
import * as Horizon from '@cloudsop/horizon/index.ts';
import * as LogUtils from '../jest/logUtils';
import { getLogUtils } from '../jest/testUtils';
describe('合成焦点事件', () => {
const LogUtils = getLogUtils();
it('onFocus', () => {
const realNode = Horizon.render(

View File

@ -1,7 +1,8 @@
import * as Horizon from '@cloudsop/horizon/index.ts';
import * as LogUtils from '../jest/logUtils';
import { getLogUtils } from '../jest/testUtils';
describe('Keyboard Event', () => {
const LogUtils = getLogUtils();
it('keydown,keypress,keyup的keycode,charcode', () => {
const node = Horizon.render(

View File

@ -1,7 +1,9 @@
import * as Horizon from '@cloudsop/horizon/index.ts';
import * as LogUtils from '../jest/logUtils';
import { getLogUtils } from '../jest/testUtils';
describe('MouseEvent Test', () => {
const LogUtils =getLogUtils();
describe('onClick Test', () => {
it('绑定this', () => {
class App extends Horizon.Component {

View File

@ -1,7 +1,9 @@
import * as Horizon from '@cloudsop/horizon/index.ts';
import * as LogUtils from '../jest/logUtils';
import { getLogUtils } from '../jest/testUtils';
describe('合成滚轮事件', () => {
const LogUtils = getLogUtils();
it('onWheel', () => {
const realNode = Horizon.render(
<div
@ -34,7 +36,7 @@ describe('合成滚轮事件', () => {
LogUtils.log(e.type + ' handle');
};
const realNode = Horizon.render(
<div onWheel={eventHandler}/>,
<div onWheel={eventHandler} />,
container
);

View File

@ -1,5 +1,7 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import * as Horizon from '@cloudsop/horizon/index.ts';
import * as LogUtils from './logUtils';
import { getLogUtils } from './testUtils';
export const App = (props) => {
const Parent = props.parent;
@ -12,9 +14,10 @@ export const App = (props) => {
</Parent>
</div>
);
}
};
export const Text = (props) => {
const LogUtils =getLogUtils();
LogUtils.log(props.text);
return <p id={props.id}>{props.text}</p>;
}
};

View File

@ -1,8 +1,9 @@
import { unmountComponentAtNode } from '../../../libs/horizon/src/dom/DOMExternal';
import * as LogUtils from '../jest/logUtils';
import failOnConsole from 'jest-fail-on-console';
import { getLogUtils } from './testUtils';
//import failOnConsole from 'jest-fail-on-console';
failOnConsole();
//failOnConsole();
const LogUtils = getLogUtils();
global.isDev = process.env.NODE_ENV === 'development';
global.container = null;
global.beforeEach(() => {

View File

@ -1,26 +0,0 @@
let dataArray = null;
const log = (value) => {
if (dataArray === null) {
dataArray = [value];
} else {
dataArray.push(value);
}
};
const getAndClear = () => {
if (dataArray === null) {
return [];
}
const values = dataArray;
dataArray = null;
return values;
};
const clear = () => {
dataArray = dataArray ? null : dataArray;
};
exports.clear = clear;
exports.log = log;
exports.getAndClear = getAndClear;

View File

@ -1,13 +1,14 @@
import { allDelegatedNativeEvents } from '../../../libs/horizon/src/event/EventCollection';
import * as LogUtils from './logUtils';
//import * as LogUtils from './logUtils';
export const stopBubbleOrCapture = (e, value) => {
LogUtils.log(value)
const LogUtils = getLogUtils();
LogUtils.log(value);
e.stopPropagation();
};
export const getEventListeners = (dom) => {
let ret = true
let ret = true;
let keyArray = [];
for (let key in dom) {
keyArray.push(key);
@ -21,9 +22,9 @@ export const getEventListeners = (dom) => {
ret = false;
throw new Error('没有挂载全量事件');
}
})
});
} catch (error) {
console.log(error);
}
return ret;
};
@ -34,3 +35,38 @@ export function triggerClickEvent(container, id) {
});
container.querySelector(`#${id}`).dispatchEvent(event);
}
class LogUtils {
constructor() {
this.dataArray = null;
}
log = (value) => {
if (this.dataArray === null) {
this.dataArray = [value];
} else {
this.dataArray.push(value);
}
};
getAndClear = () => {
if (this.dataArray === null) {
return [];
}
const values = this.dataArray;
this.dataArray = null;
return values;
};
clear = () => {
this.dataArray = this.dataArray ? null : this.dataArray;
};
}
let logger;
export function getLogUtils() {
if(!logger) {
logger = new LogUtils();
}
return logger;
}