Match-id-3dea3432fa8896d00659685b3248e77bd1c46ca1
This commit is contained in:
commit
bfb7283dc5
|
@ -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(<NullElement />, document.createElement('div'));
|
||||||
|
}).toThrow('Component type is invalid, got: null');
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
Horizon.render(<UndefinedElement />, document.createElement('div'));
|
||||||
|
}).toThrow('Component type is invalid, got: undefined');
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
return <AppChild />;
|
||||||
|
};
|
||||||
|
|
||||||
|
let AppChild = () => {
|
||||||
|
return (
|
||||||
|
<NullElement />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
Horizon.render(<App />, document.createElement('div'));
|
||||||
|
}).toThrow('Component type is invalid, got: null');
|
||||||
|
|
||||||
|
AppChild = () => {
|
||||||
|
return (
|
||||||
|
<UndefinedElement />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
Horizon.render(<App />, document.createElement('div'));
|
||||||
|
}).toThrow('Component type is invalid, got: undefined');
|
||||||
|
});
|
||||||
|
});
|
|
@ -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');
|
||||||
|
});
|
||||||
|
});
|
|
@ -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.Fragment />
|
||||||
|
);
|
||||||
|
|
||||||
|
Horizon.render(element, container);
|
||||||
|
|
||||||
|
expect(container.textContent).toBe('');
|
||||||
|
});
|
||||||
|
it('可以渲染单个元素', () => {
|
||||||
|
const element = (
|
||||||
|
<Horizon.Fragment>
|
||||||
|
<Text text="Fragment" />
|
||||||
|
</Horizon.Fragment>
|
||||||
|
);
|
||||||
|
|
||||||
|
Horizon.render(element, container);
|
||||||
|
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['Fragment']);
|
||||||
|
expect(container.textContent).toBe('Fragment');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('可以渲染混合元素', () => {
|
||||||
|
const element = (
|
||||||
|
<Horizon.Fragment>
|
||||||
|
Java and <Text text="JavaScript" />
|
||||||
|
</Horizon.Fragment>
|
||||||
|
);
|
||||||
|
|
||||||
|
Horizon.render(element, container);
|
||||||
|
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['JavaScript']);
|
||||||
|
expect(container.textContent).toBe('Java and JavaScript');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('可以渲染集合元素', () => {
|
||||||
|
const App = [<Text text="Java" />, <Text text="JavaScript" />];
|
||||||
|
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 <p>{props.logo}</p>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = (props) => {
|
||||||
|
return props.change ? (
|
||||||
|
<>
|
||||||
|
<ChildApp logo={1} />
|
||||||
|
</>
|
||||||
|
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<ChildApp logo={2} />
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, container);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={false} />, container);
|
||||||
|
});
|
||||||
|
// 切换到不同层级Fragment时,副作用状态不会保留
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
expect(container.textContent).toBe('2');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, 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 <p>{props.logo}</p>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = (props) => {
|
||||||
|
return props.change ? (
|
||||||
|
<ChildApp logo={1} />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<ChildApp logo={2} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, container);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={false} />, container);
|
||||||
|
});
|
||||||
|
// 状态会保留
|
||||||
|
expect(LogUtils.getNotClear()).toEqual(['useEffect']);
|
||||||
|
expect(container.textContent).toBe('2');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, 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 <p>{props.logo}</p>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = (props) => {
|
||||||
|
return props.change ? (
|
||||||
|
<ChildApp logo={1} />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div>123</div>
|
||||||
|
<ChildApp logo={2} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, container);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={false} />, container);
|
||||||
|
});
|
||||||
|
// 状态不会保留
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
expect(container.textContent).toBe('1232');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, 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 <p>{props.logo}</p>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = (props) => {
|
||||||
|
return props.change ? (
|
||||||
|
<ChildApp logo={1} />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<ChildApp logo={2} />
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, container);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={false} />, container);
|
||||||
|
});
|
||||||
|
// 状态不会保留
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
expect(container.textContent).toBe('2');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, 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 <p>{props.logo}</p>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = (props) => {
|
||||||
|
return props.change ? (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<ChildApp logo={1} />
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<ChildApp logo={2} />
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, container);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={false} />, container);
|
||||||
|
});
|
||||||
|
// 状态会保留
|
||||||
|
expect(LogUtils.getNotClear()).toEqual(['useEffect']);
|
||||||
|
expect(container.textContent).toBe('2');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, 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 <p>{props.logo}</p>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = (props) => {
|
||||||
|
return props.change ? (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<ChildApp logo={1} />
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
{[<ChildApp logo={2} />]}
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, container);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={false} />, container);
|
||||||
|
});
|
||||||
|
// 状态会保留
|
||||||
|
expect(LogUtils.getNotClear()).toEqual(['useEffect']);
|
||||||
|
expect(container.textContent).toBe('2');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, 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 <p>{props.logo}</p>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = (props) => {
|
||||||
|
return props.change ? (
|
||||||
|
[<ChildApp logo={1} />]
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<ChildApp logo={2} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, container);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={false} />, container);
|
||||||
|
});
|
||||||
|
// 状态会保留
|
||||||
|
expect(LogUtils.getNotClear()).toEqual(['useEffect']);
|
||||||
|
expect(container.textContent).toBe('2');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, 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 <p>{props.logo}</p>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = (props) => {
|
||||||
|
return props.change ? (
|
||||||
|
<>
|
||||||
|
[<ChildApp logo={1} />]
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<>
|
||||||
|
<ChildApp logo={2} />
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, container);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={false} />, container);
|
||||||
|
});
|
||||||
|
// 状态会保留
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
expect(container.textContent).toBe('2');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, 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 <p>{props.logo}</p>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App = (props) => {
|
||||||
|
return props.change ? (
|
||||||
|
<Horizon.Fragment key='hf'>
|
||||||
|
<ChildApp logo={1} />
|
||||||
|
</Horizon.Fragment>
|
||||||
|
) : (
|
||||||
|
<Horizon.Fragment key='nhf'>
|
||||||
|
<ChildApp logo={2} />
|
||||||
|
</Horizon.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, container);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={false} />, container);
|
||||||
|
});
|
||||||
|
// 状态不会保留
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
expect(container.textContent).toBe('2');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
Horizon.render(<App change={true} />, container);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getNotClear()).toEqual([]);
|
||||||
|
expect(container.textContent).toBe('1');
|
||||||
|
});
|
||||||
|
});
|
|
@ -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);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -1,5 +1,5 @@
|
||||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
import * as LogUtils from '../../jest/logUtils';
|
import { getLogUtils } from '../../jest/testUtils';
|
||||||
import { Text } from '../../jest/commonComponents';
|
import { Text } from '../../jest/commonComponents';
|
||||||
|
|
||||||
describe('useEffect Hook Test', () => {
|
describe('useEffect Hook Test', () => {
|
||||||
|
@ -12,6 +12,7 @@ describe('useEffect Hook Test', () => {
|
||||||
act,
|
act,
|
||||||
} = Horizon;
|
} = Horizon;
|
||||||
|
|
||||||
|
const LogUtils = getLogUtils();
|
||||||
it('简单使用useEffect', () => {
|
it('简单使用useEffect', () => {
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [num, setNum] = useState(0);
|
const [num, setNum] = useState(0);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
import * as LogUtils from '../../jest/logUtils';
|
|
||||||
import { Text } from '../../jest/commonComponents';
|
import { Text } from '../../jest/commonComponents';
|
||||||
|
import { getLogUtils } from '../../jest/testUtils';
|
||||||
|
|
||||||
describe('useImperativeHandle Hook Test', () => {
|
describe('useImperativeHandle Hook Test', () => {
|
||||||
const {
|
const {
|
||||||
|
@ -10,7 +10,7 @@ describe('useImperativeHandle Hook Test', () => {
|
||||||
act,
|
act,
|
||||||
} = Horizon;
|
} = Horizon;
|
||||||
const { unmountComponentAtNode } = Horizon;
|
const { unmountComponentAtNode } = Horizon;
|
||||||
|
const LogUtils = getLogUtils();
|
||||||
it('测试useImperativeHandle', () => {
|
it('测试useImperativeHandle', () => {
|
||||||
|
|
||||||
let App = (props, ref) => {
|
let App = (props, ref) => {
|
||||||
|
@ -48,7 +48,6 @@ describe('useImperativeHandle Hook Test', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('useImperativeHandle没有配置dep时自动更新', () => {
|
it('useImperativeHandle没有配置dep时自动更新', () => {
|
||||||
|
|
||||||
let App = (props, ref) => {
|
let App = (props, ref) => {
|
||||||
const [num, setNum] = useState(0);
|
const [num, setNum] = useState(0);
|
||||||
useImperativeHandle(ref, () => ({ num, setNum }));
|
useImperativeHandle(ref, () => ({ num, setNum }));
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
import * as LogUtils from '../../jest/logUtils';
|
import { getLogUtils } from '../../jest/testUtils';
|
||||||
import { Text } from '../../jest/commonComponents';
|
import { Text } from '../../jest/commonComponents';
|
||||||
|
|
||||||
describe('useLayoutEffect Hook Test', () => {
|
describe('useLayoutEffect Hook Test', () => {
|
||||||
|
@ -9,7 +9,7 @@ describe('useLayoutEffect Hook Test', () => {
|
||||||
useLayoutEffect,
|
useLayoutEffect,
|
||||||
act,
|
act,
|
||||||
} = Horizon;
|
} = Horizon;
|
||||||
|
const LogUtils = getLogUtils();
|
||||||
it('简单使用useLayoutEffect', () => {
|
it('简单使用useLayoutEffect', () => {
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [num, setNum] = useState(0);
|
const [num, setNum] = useState(0);
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
import * as LogUtils from '../../jest/logUtils';
|
import { getLogUtils } from '../../jest/testUtils';
|
||||||
import { Text } from '../../jest/commonComponents';
|
import { Text } from '../../jest/commonComponents';
|
||||||
|
|
||||||
describe('useMemo Hook Test', () => {
|
describe('useMemo Hook Test', () => {
|
||||||
const { useMemo, useState } = Horizon;
|
const { useMemo, useState } = Horizon;
|
||||||
|
const LogUtils = getLogUtils();
|
||||||
|
|
||||||
it('测试useMemo', () => {
|
it('测试useMemo', () => {
|
||||||
let setMemo;
|
let setMemo;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
import * as LogUtils from '../../jest/logUtils';
|
import { getLogUtils } from '../../jest/testUtils';
|
||||||
import { Text } from '../../jest/commonComponents';
|
import { Text } from '../../jest/commonComponents';
|
||||||
|
|
||||||
describe('useRef Hook Test', () => {
|
describe('useRef Hook Test', () => {
|
||||||
const { useState, useRef } = Horizon;
|
const { useState, useRef } = Horizon;
|
||||||
|
const LogUtils = getLogUtils();
|
||||||
|
|
||||||
it('测试useRef', () => {
|
it('测试useRef', () => {
|
||||||
const App = () => {
|
const App = () => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
import * as LogUtils from '../../jest/logUtils';
|
import { getLogUtils } from '../../jest/testUtils';
|
||||||
import { Text } from '../../jest/commonComponents';
|
import { Text } from '../../jest/commonComponents';
|
||||||
|
|
||||||
describe('useState Hook Test', () => {
|
describe('useState Hook Test', () => {
|
||||||
|
@ -10,6 +10,7 @@ describe('useState Hook Test', () => {
|
||||||
memo,
|
memo,
|
||||||
act,
|
act,
|
||||||
} = Horizon;
|
} = Horizon;
|
||||||
|
const LogUtils = getLogUtils();
|
||||||
|
|
||||||
it('简单使用useState', () => {
|
it('简单使用useState', () => {
|
||||||
const App = () => {
|
const App = () => {
|
||||||
|
@ -112,7 +113,7 @@ describe('useState Hook Test', () => {
|
||||||
|
|
||||||
it('useState与memo一起使用', () => {
|
it('useState与memo一起使用', () => {
|
||||||
let setNum;
|
let setNum;
|
||||||
const App = memo((props) => {
|
const App = memo(() => {
|
||||||
const [num, _setNum] = useState(0);
|
const [num, _setNum] = useState(0);
|
||||||
setNum = _setNum;
|
setNum = _setNum;
|
||||||
return <Text text={num} />;
|
return <Text text={num} />;
|
||||||
|
|
|
@ -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 <span>{text}</span>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Lazy = Horizon.lazy(() => mockImport(LazyComponent));
|
||||||
|
|
||||||
|
Horizon.render(
|
||||||
|
<Horizon.Suspense fallback={<Text text="Loading..." />}>
|
||||||
|
<Lazy greeting="Hi" />
|
||||||
|
</Horizon.Suspense>,
|
||||||
|
container
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['Loading...']);
|
||||||
|
expect(container.textContent).toBe('Loading...');
|
||||||
|
expect(container.querySelector('span')).toBe(null);
|
||||||
|
|
||||||
|
await Promise.resolve();
|
||||||
|
Horizon.render(
|
||||||
|
<Horizon.Suspense fallback={<Text text="Loading..." />}>
|
||||||
|
<Lazy greeting="Goodbye" />
|
||||||
|
</Horizon.Suspense>,
|
||||||
|
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(
|
||||||
|
<Horizon.Suspense fallback={<div>Loading...</div>}>
|
||||||
|
<LazyApp text="Lazy" />
|
||||||
|
</Horizon.Suspense>,
|
||||||
|
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
|
||||||
|
? <h2>Error: {this.state.message}</h2>
|
||||||
|
: this.props.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const LazyComponent = () => {
|
||||||
|
const [num, setNum] = Horizon.useState(0);
|
||||||
|
if (num === 2) {
|
||||||
|
throw new Error('num is 2');
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p id="p">{num}</p>
|
||||||
|
<button onClick={() => setNum(num + 1)} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const LazyApp = Horizon.lazy(() => mockImport(LazyComponent));
|
||||||
|
|
||||||
|
Horizon.render(
|
||||||
|
<ErrorBoundary>
|
||||||
|
<Horizon.Suspense fallback={<div>Loading...</div>}>
|
||||||
|
<LazyApp />
|
||||||
|
</Horizon.Suspense>
|
||||||
|
</ErrorBoundary>,
|
||||||
|
container
|
||||||
|
);
|
||||||
|
expect(container.textContent).toBe('Loading...');
|
||||||
|
|
||||||
|
await Promise.resolve();
|
||||||
|
Horizon.render(
|
||||||
|
<ErrorBoundary>
|
||||||
|
<Horizon.Suspense fallback={<Text text="Loading..." />}>
|
||||||
|
<LazyApp />
|
||||||
|
</Horizon.Suspense>
|
||||||
|
</ErrorBoundary>,
|
||||||
|
container
|
||||||
|
);
|
||||||
|
expect(container.textContent).toBe('0');
|
||||||
|
container.querySelector('button').click();
|
||||||
|
expect(container.textContent).toBe('1');
|
||||||
|
jest.spyOn(console, 'error').mockImplementation();
|
||||||
|
container.querySelector('button').click();
|
||||||
|
expect(container.textContent).toBe('Error: num is 2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('componentDidCatch捕获异常', async () => {
|
||||||
|
class ErrorBoundary extends Horizon.Component {
|
||||||
|
state = {
|
||||||
|
catchError: false,
|
||||||
|
error: null,
|
||||||
|
componentStack: null
|
||||||
|
};
|
||||||
|
componentDidCatch(error, info) {
|
||||||
|
if(error){
|
||||||
|
this.setState({
|
||||||
|
catchError: true,
|
||||||
|
error,
|
||||||
|
componentStack: info.componentStack
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return this.state.catchError
|
||||||
|
? <h2>Error: {this.state.error.message}</h2>
|
||||||
|
: this.props.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const LazyComponent = () => {
|
||||||
|
const [num, setNum] = Horizon.useState(0);
|
||||||
|
if (num === 2) {
|
||||||
|
throw new Error('num is 2');
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p id="p">{num}</p>
|
||||||
|
<button onClick={() => setNum(num + 1)} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const LazyApp = Horizon.lazy(() => mockImport(LazyComponent));
|
||||||
|
|
||||||
|
Horizon.render(
|
||||||
|
<ErrorBoundary>
|
||||||
|
<Horizon.Suspense fallback={<div>Loading...</div>}>
|
||||||
|
<LazyApp />
|
||||||
|
</Horizon.Suspense>
|
||||||
|
</ErrorBoundary>,
|
||||||
|
container
|
||||||
|
);
|
||||||
|
expect(container.textContent).toBe('Loading...');
|
||||||
|
|
||||||
|
await Promise.resolve();
|
||||||
|
Horizon.render(
|
||||||
|
<ErrorBoundary>
|
||||||
|
<Horizon.Suspense fallback={<Text text="Loading..." />}>
|
||||||
|
<LazyApp />
|
||||||
|
</Horizon.Suspense>
|
||||||
|
</ErrorBoundary>,
|
||||||
|
container
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(container.textContent).toBe('0');
|
||||||
|
container.querySelector('button').click();
|
||||||
|
expect(container.textContent).toBe('1');
|
||||||
|
jest.spyOn(console, 'error').mockImplementation();
|
||||||
|
container.querySelector('button').click();
|
||||||
|
expect(container.textContent).toBe('Error: num is 2');
|
||||||
|
});
|
||||||
|
});
|
|
@ -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'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,205 @@
|
||||||
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
|
import { getLogUtils } from '../jest/testUtils';
|
||||||
|
|
||||||
|
describe('PortalComponent Test', () => {
|
||||||
|
const LogUtils = getLogUtils();
|
||||||
|
|
||||||
|
it('将子节点渲染到存在于父组件以外的 DOM 节点', () => {
|
||||||
|
const portalRoot = document.createElement('div');
|
||||||
|
|
||||||
|
class PortalApp extends Horizon.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.element = portalRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return Horizon.createPortal(
|
||||||
|
this.props.child,
|
||||||
|
this.element,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Horizon.render(<PortalApp child={<div>PortalApp</div>} />, container);
|
||||||
|
expect(container.textContent).toBe('');
|
||||||
|
// <div>PortalApp</div>被渲染到了portalRoot而非container
|
||||||
|
expect(portalRoot.textContent).toBe('PortalApp');
|
||||||
|
|
||||||
|
Horizon.unmountComponentAtNode(container);
|
||||||
|
expect(container.textContent).toBe('');
|
||||||
|
expect(portalRoot.textContent).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('渲染多个Portal', () => {
|
||||||
|
const portalRoot1st = document.createElement('div');
|
||||||
|
const portalRoot2nd = document.createElement('div');
|
||||||
|
|
||||||
|
class PortalApp extends Horizon.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.element = portalRoot1st;
|
||||||
|
this.newElement = portalRoot2nd;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return [
|
||||||
|
Horizon.createPortal(
|
||||||
|
this.props.child,
|
||||||
|
this.element,
|
||||||
|
),
|
||||||
|
Horizon.createPortal(
|
||||||
|
this.props.child,
|
||||||
|
this.newElement,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Horizon.render(<PortalApp child={<div>PortalApp</div>} />, container);
|
||||||
|
expect(container.textContent).toBe('');
|
||||||
|
// <div>PortalApp</div>被渲染到了portalRoot而非container
|
||||||
|
expect(portalRoot1st.textContent).toBe('PortalApp');
|
||||||
|
expect(portalRoot2nd.textContent).toBe('PortalApp');
|
||||||
|
|
||||||
|
Horizon.unmountComponentAtNode(container);
|
||||||
|
expect(container.textContent).toBe('');
|
||||||
|
expect(portalRoot1st.textContent).toBe('');
|
||||||
|
expect(portalRoot2nd.textContent).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('渲染最近的Portal', () => {
|
||||||
|
const portalRoot1st = document.createElement('div');
|
||||||
|
const portalRoot2nd = document.createElement('div');
|
||||||
|
const portalRoot3rd = document.createElement('div');
|
||||||
|
|
||||||
|
class PortalApp extends Horizon.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.element = portalRoot1st;
|
||||||
|
this.newElement = portalRoot2nd;
|
||||||
|
this.element3rd = portalRoot3rd;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return [
|
||||||
|
<div>PortalApp1st</div>,
|
||||||
|
Horizon.createPortal([
|
||||||
|
<div>PortalApp4</div>,
|
||||||
|
Horizon.createPortal(
|
||||||
|
this.props.child,
|
||||||
|
this.element3rd,
|
||||||
|
),
|
||||||
|
], this.element),
|
||||||
|
<div>PortalApp2nd</div>,
|
||||||
|
Horizon.createPortal(
|
||||||
|
this.props.child,
|
||||||
|
this.newElement,
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Horizon.render(<PortalApp child={<div>PortalApp</div>} />, container);
|
||||||
|
expect(container.textContent).toBe('PortalApp1stPortalApp2nd');
|
||||||
|
// <div>PortalApp4</div>会挂载在this.element上
|
||||||
|
expect(portalRoot1st.textContent).toBe('PortalApp4');
|
||||||
|
expect(portalRoot2nd.textContent).toBe('PortalApp');
|
||||||
|
expect(portalRoot3rd.textContent).toBe('PortalApp');
|
||||||
|
|
||||||
|
Horizon.unmountComponentAtNode(container);
|
||||||
|
expect(container.textContent).toBe('');
|
||||||
|
expect(portalRoot1st.textContent).toBe('');
|
||||||
|
expect(portalRoot2nd.textContent).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('改变Portal的参数', () => {
|
||||||
|
const portalRoot = document.createElement('div');
|
||||||
|
|
||||||
|
class PortalApp extends Horizon.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.element = portalRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return Horizon.createPortal(
|
||||||
|
this.props.child,
|
||||||
|
this.element,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Horizon.render(<PortalApp key='portal' child={<div>PortalApp</div>} />, container);
|
||||||
|
expect(container.textContent).toBe('');
|
||||||
|
expect(portalRoot.textContent).toBe('PortalApp');
|
||||||
|
|
||||||
|
Horizon.render(<PortalApp key='portal' child={<div>AppPortal</div>} />, container);
|
||||||
|
expect(container.textContent).toBe('');
|
||||||
|
expect(portalRoot.textContent).toBe('AppPortal');
|
||||||
|
|
||||||
|
Horizon.render(<PortalApp key='portal' child={['por', 'tal']} />, container);
|
||||||
|
expect(container.textContent).toBe('');
|
||||||
|
expect(portalRoot.textContent).toBe('portal');
|
||||||
|
|
||||||
|
Horizon.render(<PortalApp key='portal' child={null} />, container);
|
||||||
|
expect(container.textContent).toBe('');
|
||||||
|
expect(portalRoot.textContent).toBe('');
|
||||||
|
|
||||||
|
Horizon.unmountComponentAtNode(container);
|
||||||
|
expect(container.textContent).toBe('');
|
||||||
|
expect(portalRoot.textContent).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('通过Portal进行事件冒泡', () => {
|
||||||
|
const portalRoot = document.createElement('div');
|
||||||
|
const buttonRef = Horizon.createRef();
|
||||||
|
|
||||||
|
class PortalApp extends Horizon.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.element = portalRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return Horizon.createPortal(
|
||||||
|
this.props.child,
|
||||||
|
this.element,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Child = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button ref={buttonRef}>Click</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
const handleClick = () => {
|
||||||
|
LogUtils.log('bubble click event');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCaptureClick = () => {
|
||||||
|
LogUtils.log('capture click event');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div onClickCapture={handleCaptureClick()} onClick={handleClick()}>
|
||||||
|
<PortalApp child={<Child />}>
|
||||||
|
|
||||||
|
</PortalApp>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
Horizon.render(<App />, container);
|
||||||
|
const event = document.createEvent('Event');
|
||||||
|
event.initEvent('click', true, true);
|
||||||
|
buttonRef.current.dispatchEvent(event);
|
||||||
|
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
// 从外到内先捕获再冒泡
|
||||||
|
'capture click event',
|
||||||
|
'bubble click event'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,59 @@
|
||||||
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
|
import { Text } from '../jest/commonComponents';
|
||||||
|
import { getLogUtils } from '../jest/testUtils';
|
||||||
|
|
||||||
|
describe('SuspenseComponent Test', () => {
|
||||||
|
const LogUtils = getLogUtils();
|
||||||
|
const mockImport = jest.fn(async (component) => {
|
||||||
|
return { default: component };
|
||||||
|
});
|
||||||
|
|
||||||
|
// var EMPTY_OBJECT = {};
|
||||||
|
// const mockCreateResource = jest.fn((component) => {
|
||||||
|
// let result = EMPTY_OBJECT;
|
||||||
|
// return () =>{
|
||||||
|
// component().then(res => {
|
||||||
|
// LogUtils.log(res);
|
||||||
|
// result = res;
|
||||||
|
// }, reason => {
|
||||||
|
// LogUtils.log(reason);
|
||||||
|
// });
|
||||||
|
// if(result === EMPTY_OBJECT){
|
||||||
|
// throw component();
|
||||||
|
// }
|
||||||
|
// return result;
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
|
||||||
|
it('挂载lazy组件', async () => {
|
||||||
|
// 用同步的代码来实现异步操作
|
||||||
|
class LazyComponent extends Horizon.Component {
|
||||||
|
render() {
|
||||||
|
return <Text text={this.props.num} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Lazy = Horizon.lazy(() => mockImport(LazyComponent));
|
||||||
|
|
||||||
|
Horizon.render(
|
||||||
|
<Horizon.Suspense fallback={<Text text="Loading..." />}>
|
||||||
|
<Lazy num={5} />
|
||||||
|
</Horizon.Suspense>,
|
||||||
|
container
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['Loading...']);
|
||||||
|
expect(container.textContent).toBe('Loading...');
|
||||||
|
|
||||||
|
await Promise.resolve();
|
||||||
|
Horizon.render(
|
||||||
|
<Horizon.Suspense fallback={<Text text="Loading..." />}>
|
||||||
|
<Lazy num={5} />
|
||||||
|
</Horizon.Suspense>,
|
||||||
|
container
|
||||||
|
);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([5]);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('5');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -1,9 +1,10 @@
|
||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
import * as LogUtils from '../jest/logUtils';
|
import { getLogUtils } from '../jest/testUtils';
|
||||||
|
|
||||||
describe('Dom Input', () => {
|
describe('Dom Input', () => {
|
||||||
const { act } = Horizon;
|
const { act } = Horizon;
|
||||||
|
const LogUtils = getLogUtils();
|
||||||
|
|
||||||
describe('type checkbox', () => {
|
describe('type checkbox', () => {
|
||||||
it('没有设置checked属性时,控制台不会报错', () => {
|
it('没有设置checked属性时,控制台不会报错', () => {
|
||||||
|
|
|
@ -130,7 +130,7 @@ describe('Dom Select', () => {
|
||||||
expect(realNode.options[1].selected).toBe(true);
|
expect(realNode.options[1].selected).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('设置defaultValue后,select不受控', () => {
|
it('设置defaultValue后,select不受控', () => {
|
||||||
const selectNode = (
|
const selectNode = (
|
||||||
<select defaultValue={'Vue'}>
|
<select defaultValue={'Vue'}>
|
||||||
<option value='React'>React.js</option>
|
<option value='React'>React.js</option>
|
||||||
|
|
|
@ -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');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -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 />`;
|
|
@ -1,8 +1,8 @@
|
||||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
import * as LogUtils from '../jest/logUtils';
|
|
||||||
import * as TestUtils from '../jest/testUtils';
|
import * as TestUtils from '../jest/testUtils';
|
||||||
|
|
||||||
describe('事件', () => {
|
describe('事件', () => {
|
||||||
|
const LogUtils = TestUtils.getLogUtils();
|
||||||
it('根节点挂载全量事件', () => {
|
it('根节点挂载全量事件', () => {
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return <div />;
|
return <div />;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
import * as LogUtils from '../jest/logUtils';
|
import { getLogUtils } from '../jest/testUtils';
|
||||||
|
|
||||||
describe('合成焦点事件', () => {
|
describe('合成焦点事件', () => {
|
||||||
|
const LogUtils = getLogUtils();
|
||||||
|
|
||||||
it('onFocus', () => {
|
it('onFocus', () => {
|
||||||
const realNode = Horizon.render(
|
const realNode = Horizon.render(
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
import * as LogUtils from '../jest/logUtils';
|
import { getLogUtils } from '../jest/testUtils';
|
||||||
|
|
||||||
describe('Keyboard Event', () => {
|
describe('Keyboard Event', () => {
|
||||||
|
const LogUtils = getLogUtils();
|
||||||
|
|
||||||
it('keydown,keypress,keyup的keycode,charcode', () => {
|
it('keydown,keypress,keyup的keycode,charcode', () => {
|
||||||
const node = Horizon.render(
|
const node = Horizon.render(
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
import * as LogUtils from '../jest/logUtils';
|
import { getLogUtils } from '../jest/testUtils';
|
||||||
|
|
||||||
describe('MouseEvent Test', () => {
|
describe('MouseEvent Test', () => {
|
||||||
|
const LogUtils =getLogUtils();
|
||||||
|
|
||||||
describe('onClick Test', () => {
|
describe('onClick Test', () => {
|
||||||
it('绑定this', () => {
|
it('绑定this', () => {
|
||||||
class App extends Horizon.Component {
|
class App extends Horizon.Component {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
import * as LogUtils from '../jest/logUtils';
|
import { getLogUtils } from '../jest/testUtils';
|
||||||
|
|
||||||
describe('合成滚轮事件', () => {
|
describe('合成滚轮事件', () => {
|
||||||
|
const LogUtils = getLogUtils();
|
||||||
|
|
||||||
it('onWheel', () => {
|
it('onWheel', () => {
|
||||||
const realNode = Horizon.render(
|
const realNode = Horizon.render(
|
||||||
<div
|
<div
|
||||||
|
@ -34,7 +36,7 @@ describe('合成滚轮事件', () => {
|
||||||
LogUtils.log(e.type + ' handle');
|
LogUtils.log(e.type + ' handle');
|
||||||
};
|
};
|
||||||
const realNode = Horizon.render(
|
const realNode = Horizon.render(
|
||||||
<div onWheel={eventHandler}/>,
|
<div onWheel={eventHandler} />,
|
||||||
container
|
container
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||||
import * as LogUtils from './logUtils';
|
import { getLogUtils } from './testUtils';
|
||||||
|
|
||||||
export const App = (props) => {
|
export const App = (props) => {
|
||||||
const Parent = props.parent;
|
const Parent = props.parent;
|
||||||
|
@ -12,9 +14,10 @@ export const App = (props) => {
|
||||||
</Parent>
|
</Parent>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const Text = (props) => {
|
export const Text = (props) => {
|
||||||
|
const LogUtils =getLogUtils();
|
||||||
LogUtils.log(props.text);
|
LogUtils.log(props.text);
|
||||||
return <p id={props.id}>{props.text}</p>;
|
return <p id={props.id}>{props.text}</p>;
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { unmountComponentAtNode } from '../../../libs/horizon/src/dom/DOMExternal';
|
import { unmountComponentAtNode } from '../../../libs/horizon/src/dom/DOMExternal';
|
||||||
import * as LogUtils from '../jest/logUtils';
|
import { getLogUtils } from './testUtils';
|
||||||
import failOnConsole from 'jest-fail-on-console';
|
//import failOnConsole from 'jest-fail-on-console';
|
||||||
|
|
||||||
failOnConsole();
|
//failOnConsole();
|
||||||
|
const LogUtils = getLogUtils();
|
||||||
global.isDev = process.env.NODE_ENV === 'development';
|
global.isDev = process.env.NODE_ENV === 'development';
|
||||||
global.container = null;
|
global.container = null;
|
||||||
global.beforeEach(() => {
|
global.beforeEach(() => {
|
||||||
|
|
|
@ -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;
|
|
|
@ -1,31 +1,66 @@
|
||||||
import { allDelegatedNativeEvents } from '../../../libs/horizon/src/event/EventCollection';
|
import { allDelegatedNativeEvents } from '../../../libs/horizon/src/event/EventCollection';
|
||||||
import * as LogUtils from './logUtils';
|
//import * as LogUtils from './logUtils';
|
||||||
|
|
||||||
export const stopBubbleOrCapture = (e, value) => {
|
export const stopBubbleOrCapture = (e, value) => {
|
||||||
LogUtils.log(value)
|
const LogUtils = getLogUtils();
|
||||||
|
LogUtils.log(value);
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getEventListeners = (dom) => {
|
function listAllEventListeners() {
|
||||||
let ret = true
|
const allElements = Array.prototype.slice.call(document.querySelectorAll('*'));
|
||||||
let keyArray = [];
|
allElements.push(document);
|
||||||
for (let key in dom) {
|
allElements.push(window);
|
||||||
keyArray.push(key);
|
|
||||||
}
|
|
||||||
console.log(keyArray);
|
|
||||||
console.log('---------------------------------');
|
|
||||||
console.log(allDelegatedNativeEvents);
|
|
||||||
try {
|
|
||||||
allDelegatedNativeEvents.forEach(event => {
|
|
||||||
if (!keyArray.includes(event)) {
|
|
||||||
ret = false;
|
|
||||||
throw new Error('没有挂载全量事件');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
|
|
||||||
|
const types = [];
|
||||||
|
|
||||||
|
for (let ev in window) {
|
||||||
|
if (/^on/.test(ev)) types[types.length] = ev;
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
|
let elements = [];
|
||||||
|
for (let i = 0; i < allElements.length; i++) {
|
||||||
|
const currentElement = allElements[i];
|
||||||
|
for (let j = 0; j < types.length; j++) {
|
||||||
|
if (typeof currentElement[types[j]] === 'function') {
|
||||||
|
elements.push({
|
||||||
|
'node': currentElement,
|
||||||
|
'type': types[j],
|
||||||
|
'func': currentElement[types[j]].toString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements.sort(function(a,b) {
|
||||||
|
return a.type.localeCompare(b.type);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getEventListeners = (dom) => {
|
||||||
|
console.table(listAllEventListeners());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// let ret = true;
|
||||||
|
// let keyArray = [];
|
||||||
|
// for (let key in dom) {
|
||||||
|
// if (/^on/.test(key)) keyArray.push(key);
|
||||||
|
// }
|
||||||
|
// console.log(getEventListeners);
|
||||||
|
// console.log('---------------------------------');
|
||||||
|
// console.log(allDelegatedNativeEvents);
|
||||||
|
// try {
|
||||||
|
// allDelegatedNativeEvents.forEach(event => {
|
||||||
|
// if (!keyArray.includes(event)) {
|
||||||
|
// ret = false;
|
||||||
|
// throw new Error('没有挂载全量事件');
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// } catch (error) {
|
||||||
|
// console.log(error);
|
||||||
|
// }
|
||||||
|
// return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function triggerClickEvent(container, id) {
|
export function triggerClickEvent(container, id) {
|
||||||
|
@ -34,3 +69,42 @@ export function triggerClickEvent(container, id) {
|
||||||
});
|
});
|
||||||
container.querySelector(`#${id}`).dispatchEvent(event);
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
getNotClear = () => {
|
||||||
|
return this.dataArray === null ? [] : this.dataArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
clear = () => {
|
||||||
|
this.dataArray = this.dataArray ? null : this.dataArray;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let logger;
|
||||||
|
export function getLogUtils() {
|
||||||
|
if(!logger) {
|
||||||
|
logger = new LogUtils();
|
||||||
|
}
|
||||||
|
return logger;
|
||||||
|
}
|
Loading…
Reference in New Issue