Match-id-3b246bb9b81bf8b7b1fbd2ad77389f22a39a7c6d
This commit is contained in:
commit
17ec96802e
|
@ -58,15 +58,7 @@ module.exports = {
|
||||||
// globalTeardown: undefined,
|
// globalTeardown: undefined,
|
||||||
|
|
||||||
// A set of global variables that need to be available in all test environments
|
// A set of global variables that need to be available in all test environments
|
||||||
// globals: {
|
// globals: {},
|
||||||
// 'isDev': process.env.NODE_ENV === 'development',
|
|
||||||
// 'MessageChannel': function MessageChannel() {
|
|
||||||
// this.port1 = {};
|
|
||||||
// this.port2 = {
|
|
||||||
// postMessage() {}
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
|
|
||||||
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
||||||
// maxWorkers: "50%",
|
// maxWorkers: "50%",
|
||||||
|
@ -127,7 +119,7 @@ module.exports = {
|
||||||
// runner: "jest-runner",
|
// runner: "jest-runner",
|
||||||
|
|
||||||
// The paths to modules that run some code to configure or set up the testing environment before each test
|
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||||
//setupFiles: [],
|
setupFiles: [require.resolve('./scripts/__tests__/jest/jestEnvironment.js')],
|
||||||
|
|
||||||
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||||
setupFilesAfterEnv: [require.resolve('./scripts/__tests__/jest/jestSetting.js')],
|
setupFilesAfterEnv: [require.resolve('./scripts/__tests__/jest/jestSetting.js')],
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* eslint-disable no-undef */
|
||||||
|
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||||
|
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||||
|
|
||||||
|
describe('useCallback Hook Test', () => {
|
||||||
|
const { useState, useCallback } = React;
|
||||||
|
|
||||||
|
it('测试useCallback', () => {
|
||||||
|
const App = (props) => {
|
||||||
|
const [num, setNum] = useState(0);
|
||||||
|
const NumUseCallback = useCallback(() => {
|
||||||
|
setNum(num + props.text)
|
||||||
|
}, [props]);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>{num}</p>
|
||||||
|
<button onClick={NumUseCallback} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
HorizonDOM.render(<App text={1} />, container);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('0');
|
||||||
|
// 点击按钮触发num加1
|
||||||
|
container.querySelector('button').click();
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||||
|
// 再次点击,依赖项没变,num不增加
|
||||||
|
container.querySelector('button').click();
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||||
|
|
||||||
|
HorizonDOM.render(<App text={2} />, container);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||||
|
// 依赖项有变化,点击按钮num增加
|
||||||
|
container.querySelector('button').click();
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('3');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,52 @@
|
||||||
|
/* eslint-disable no-undef */
|
||||||
|
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||||
|
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||||
|
import { act } from '../../jest/customMatcher';
|
||||||
|
|
||||||
|
describe('useContext Hook Test', () => {
|
||||||
|
const { useState, useContext } = React;
|
||||||
|
const { unmountComponentAtNode } = HorizonDOM;
|
||||||
|
|
||||||
|
it('简单使用useContext', () => {
|
||||||
|
const LanguageTypes = {
|
||||||
|
JAVA: 'Java',
|
||||||
|
JAVASCRIPT: 'JavaScript',
|
||||||
|
};
|
||||||
|
const defaultValue = { type: LanguageTypes.JAVASCRIPT };
|
||||||
|
const SystemLanguageContext = React.createContext(defaultValue);
|
||||||
|
|
||||||
|
const SystemLanguageProvider = ({ type, children }) => {
|
||||||
|
return (
|
||||||
|
<SystemLanguageContext.Provider value={{ type }}>
|
||||||
|
{children}
|
||||||
|
</SystemLanguageContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const TestFunction = () => {
|
||||||
|
const context = useContext(SystemLanguageContext);
|
||||||
|
return <p id="p">{context.type}</p>;
|
||||||
|
}
|
||||||
|
let setValue;
|
||||||
|
const App = () => {
|
||||||
|
const [value, _setValue] = useState(LanguageTypes.JAVA);
|
||||||
|
setValue = _setValue;
|
||||||
|
return (
|
||||||
|
<div className="App">
|
||||||
|
<SystemLanguageProvider type={value}>
|
||||||
|
<TestFunction />
|
||||||
|
</SystemLanguageProvider>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
HorizonDOM.render(<TestFunction />, container);
|
||||||
|
// 测试当Provider未提供时,获取到的默认值'JavaScript'。
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('JavaScript');
|
||||||
|
unmountComponentAtNode(container);
|
||||||
|
HorizonDOM.render(<App />, container);
|
||||||
|
// 测试当Provider提供时,可以获取到Provider的值'Java'。
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('Java');
|
||||||
|
// 测试当Provider改变时,可以获取到最新Provider的值。
|
||||||
|
act(() => setValue(LanguageTypes.JAVASCRIPT));
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('JavaScript');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,740 @@
|
||||||
|
/* eslint-disable no-undef */
|
||||||
|
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||||
|
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||||
|
import * as LogUtils from '../../jest/logUtils';
|
||||||
|
import { act } from '../../jest/customMatcher';
|
||||||
|
import Text from '../../jest/Text';
|
||||||
|
|
||||||
|
describe('useEffect Hook Test', () => {
|
||||||
|
const {
|
||||||
|
useEffect,
|
||||||
|
useLayoutEffect,
|
||||||
|
useState,
|
||||||
|
memo,
|
||||||
|
forwardRef
|
||||||
|
} = React;
|
||||||
|
|
||||||
|
it('简单使用useEffect', () => {
|
||||||
|
const App = () => {
|
||||||
|
const [num, setNum] = useState(0);
|
||||||
|
useEffect(() => {
|
||||||
|
document.getElementById('p').style.display = num === 0 ? 'none' : 'inline';
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p style={{ display: 'block' }} id="p">{num}</p>
|
||||||
|
<button onClick={() => setNum(num + 1)} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
HorizonDOM.render(<App />, container);
|
||||||
|
expect(document.getElementById('p').style.display).toBe('block');
|
||||||
|
// 点击按钮触发num加1
|
||||||
|
container.querySelector('button').click();
|
||||||
|
expect(document.getElementById('p').style.display).toBe('none');
|
||||||
|
container.querySelector('button').click();
|
||||||
|
expect(container.querySelector('p').style.display).toBe('inline');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('act方法', () => {
|
||||||
|
const App = () => {
|
||||||
|
return <Text text={'op'} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App />, container, () => {
|
||||||
|
LogUtils.log('num effect');
|
||||||
|
});
|
||||||
|
// 第一次渲染为同步,所以同步执行的可以写在act里做判断
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['op', 'num effect']);
|
||||||
|
expect(container.textContent).toBe('op');
|
||||||
|
});
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(null, container, () => {
|
||||||
|
LogUtils.log('num effect89');
|
||||||
|
});
|
||||||
|
// 第二次渲染为异步,所以同步执行的不可以写在act里做判断,act里拿到的为空数组
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['num effect89']);
|
||||||
|
expect(container.textContent).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('兄弟节点被删除,useEffect依然正常', () => {
|
||||||
|
const App = () => {
|
||||||
|
return <Text text="App" />;
|
||||||
|
}
|
||||||
|
const NewApp = () => {
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`NewApp effect`);
|
||||||
|
}, []);
|
||||||
|
return <Text text="NewApp" />;
|
||||||
|
}
|
||||||
|
const na = <NewApp />;
|
||||||
|
// <App />必须设置key值,否则在diff的时候na会被视为不同组件
|
||||||
|
HorizonDOM.render([<App key="app" />, na], container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'App',
|
||||||
|
'NewApp'
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toBe('AppNewApp');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
// 在执行新的render前,会执行完上一次render的useEffect,所以LogUtils会加入'NewApp effect'。
|
||||||
|
HorizonDOM.render([na], container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['NewApp effect']);
|
||||||
|
expect(container.textContent).toBe('NewApp');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('兄弟节点更新,useEffect依然正常', () => {
|
||||||
|
const App = () => {
|
||||||
|
const [num, setNum] = useState(0);
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (num === 0) {
|
||||||
|
setNum(1);
|
||||||
|
}
|
||||||
|
LogUtils.log('App Layout effect ' + num);
|
||||||
|
});
|
||||||
|
return <Text text="App" />;
|
||||||
|
}
|
||||||
|
const NewApp = () => {
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`NewApp effect`);
|
||||||
|
}, []);
|
||||||
|
return <Text text="NewApp" />;
|
||||||
|
}
|
||||||
|
// <App />必须设置key值,否则在diff的时候na会被视为不同组件
|
||||||
|
HorizonDOM.render([<App key="app" />, <NewApp />], container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'App',
|
||||||
|
'NewApp',
|
||||||
|
'App Layout effect 0',
|
||||||
|
// 在App更新前,会执行完NewApp的useEffect
|
||||||
|
'NewApp effect',
|
||||||
|
'App',
|
||||||
|
'App Layout effect 1',
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toBe('AppNewApp');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('兄弟节点执行新的挂载动作,useEffect依然正常', () => {
|
||||||
|
const newContainer = document.createElement('div');
|
||||||
|
const App = () => {
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
LogUtils.log('App Layout effect');
|
||||||
|
HorizonDOM.render(<Text text="NewContainer" />, newContainer);
|
||||||
|
});
|
||||||
|
return <Text text="App" />;
|
||||||
|
}
|
||||||
|
const NewApp = () => {
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`NewApp effect`);
|
||||||
|
}, []);
|
||||||
|
return <Text text="NewApp" />;
|
||||||
|
}
|
||||||
|
// <App />必须设置key值,否则在diff的时候na会被视为不同组件
|
||||||
|
HorizonDOM.render([<App key="app" />, <NewApp />], container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'App',
|
||||||
|
'NewApp',
|
||||||
|
'App Layout effect',
|
||||||
|
// 在执行useLayoutEffectApp的render前,会执行完NewApp的useEffect
|
||||||
|
'NewApp effect',
|
||||||
|
'NewContainer',
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toBe('AppNewApp');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('执行新render的useEffect前会先执行旧render的useEffect', () => {
|
||||||
|
const App = (props) => {
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`First effect [${props.num}]`);
|
||||||
|
});
|
||||||
|
return <Text text={'num: ' + props.num} />;
|
||||||
|
}
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['num: 0', 'callback effect']);
|
||||||
|
expect(container.textContent).toEqual('num: 0');
|
||||||
|
})
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['First effect [0]']);
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
|
||||||
|
})
|
||||||
|
// 此时异步执行,act执行完后会执行新render的useEffect
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num: 1',
|
||||||
|
'callback effect',
|
||||||
|
'First effect [1]'
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toEqual('num: 1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('混合使用useEffect', () => {
|
||||||
|
const App = (props) => {
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`First effect [${props.num}]`);
|
||||||
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`Second effect [${props.num}]`);
|
||||||
|
});
|
||||||
|
return <Text text={'num: ' + props.num} />;
|
||||||
|
}
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['num: 0', 'callback effect']);
|
||||||
|
expect(container.textContent).toEqual('num: 0');
|
||||||
|
})
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['First effect [0]', 'Second effect [0]']);
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
})
|
||||||
|
// 第二次render时异步执行,act保证所有效果都已更新,所以先常规记录日志
|
||||||
|
// 然后记录useEffect的日志
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num: 1',
|
||||||
|
'callback effect',
|
||||||
|
'First effect [1]',
|
||||||
|
'Second effect [1]'
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toEqual('num: 1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('创建,销毁useEffect', () => {
|
||||||
|
const App = (props) => {
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`num effect [${props.num}]`);
|
||||||
|
return () => {
|
||||||
|
LogUtils.log('num effect destroy');
|
||||||
|
};
|
||||||
|
}, [props.num]);
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`word effect [${props.word}]`);
|
||||||
|
return () => {
|
||||||
|
LogUtils.log('word effect destroy');
|
||||||
|
};
|
||||||
|
}, [props.word]);
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
LogUtils.log(`num Layouteffect [${props.num}]`);
|
||||||
|
return () => {
|
||||||
|
LogUtils.log('num Layouteffect destroy');
|
||||||
|
};
|
||||||
|
}, [props.num]);
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
LogUtils.log(`word Layouteffect [${props.word}]`);
|
||||||
|
return () => {
|
||||||
|
LogUtils.log('word Layouteffect destroy');
|
||||||
|
};
|
||||||
|
}, [props.word]);
|
||||||
|
return <Text text={'num: ' + props.num + ',word: ' + props.word} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={0} word={'App'} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num: 0,word: App',
|
||||||
|
'num Layouteffect [0]',
|
||||||
|
'word Layouteffect [App]',
|
||||||
|
'callback effect'
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num effect [0]',
|
||||||
|
'word effect [App]',
|
||||||
|
]);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
// 此时word改变,num不变
|
||||||
|
HorizonDOM.render(<App num={0} word={'React'} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num: 0,word: React',
|
||||||
|
'word Layouteffect destroy',
|
||||||
|
'word Layouteffect [React]',
|
||||||
|
'callback effect',
|
||||||
|
// 最后执行异步的
|
||||||
|
'word effect destroy',
|
||||||
|
'word effect [React]',
|
||||||
|
]);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
// 此时num和word的所有effect都销毁
|
||||||
|
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num Layouteffect destroy',
|
||||||
|
'word Layouteffect destroy',
|
||||||
|
'callback effect',
|
||||||
|
// 最后执行异步useEffect
|
||||||
|
'num effect destroy',
|
||||||
|
'word effect destroy',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('销毁不含依赖数组的useEffect', () => {
|
||||||
|
const App = (props) => {
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`num effect [${props.num}]`);
|
||||||
|
return () => {
|
||||||
|
LogUtils.log('num effect destroy');
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return <Text text={'num: ' + props.num} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num: 0',
|
||||||
|
'callback effect'
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toEqual('num: 0');
|
||||||
|
})
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num effect [0]',
|
||||||
|
]);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num: 1',
|
||||||
|
'callback effect',
|
||||||
|
// 最后执行异步
|
||||||
|
'num effect destroy',
|
||||||
|
'num effect [1]',
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toEqual('num: 1');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'callback effect',
|
||||||
|
'num effect destroy'
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toEqual('');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('销毁依赖空数组的useEffect', () => {
|
||||||
|
const App = (props) => {
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`num effect [${props.num}]`);
|
||||||
|
return () => {
|
||||||
|
LogUtils.log('num effect destroy');
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return <Text text={'num: ' + props.num} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num: 0',
|
||||||
|
'callback effect'
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toEqual('num: 0');
|
||||||
|
})
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num effect [0]',
|
||||||
|
]);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num: 1',
|
||||||
|
'callback effect'
|
||||||
|
// 依赖空数组,没有执行useEffect
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toEqual('num: 1');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'callback effect',
|
||||||
|
'num effect destroy'
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toEqual('');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('useEffect里使用useState(1', () => {
|
||||||
|
let setNum;
|
||||||
|
const App = () => {
|
||||||
|
const [num, _setNum] = React.useState(0);
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`num effect [${num}]`);
|
||||||
|
setNum = () => _setNum(1);
|
||||||
|
}, [num]);
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
LogUtils.log(`num Layouteffect [${num}]`);
|
||||||
|
return () => {
|
||||||
|
LogUtils.log('num Layouteffect destroy');
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return <Text text={'num: ' + num} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num: 0',
|
||||||
|
'num Layouteffect [0]',
|
||||||
|
'callback effect'
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num effect [0]',
|
||||||
|
]);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
setNum();
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num: 1',
|
||||||
|
'num effect [1]'
|
||||||
|
]);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('useEffect里使用useState(2', () => {
|
||||||
|
let setNum;
|
||||||
|
const App = () => {
|
||||||
|
const [num, _setNum] = useState(0);
|
||||||
|
setNum = _setNum;
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`App effect`);
|
||||||
|
setNum(1);
|
||||||
|
}, []);
|
||||||
|
return <Text text={'Num: ' + num} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizonDOM.render(<App />, container, () => LogUtils.log('App callback effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['Num: 0', 'App callback effect']);
|
||||||
|
expect(container.textContent).toEqual('Num: 0');
|
||||||
|
act(() => {
|
||||||
|
setNum(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 虽然执行了setNum(2),但执行到setNum(1),所以最终num为1
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'App effect',
|
||||||
|
'Num: 1',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(container.textContent).toEqual('Num: 1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('useEffect与memo一起使用(1', () => {
|
||||||
|
let setNum;
|
||||||
|
const App = memo(() => {
|
||||||
|
const [num, _setNum] = useState(0);
|
||||||
|
setNum = _setNum;
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`num effect [${num}]`);
|
||||||
|
return () => {
|
||||||
|
LogUtils.log(`num effect destroy ${num}`);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return <Text text={num} />;
|
||||||
|
})
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App />, container, () => LogUtils.log('callback effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
0,
|
||||||
|
'callback effect'
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toEqual('0');
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['num effect [0]']);
|
||||||
|
|
||||||
|
// 不会重新渲染
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App />, container, () => LogUtils.log('callback effect'));
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['callback effect']);
|
||||||
|
expect(container.textContent).toEqual('0');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
|
||||||
|
// 会重新渲染
|
||||||
|
act(() => {
|
||||||
|
setNum(1);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
1,
|
||||||
|
'num effect destroy 0',
|
||||||
|
'num effect [1]'
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toEqual('1');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'callback effect',
|
||||||
|
'num effect destroy 1'
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toEqual('');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('useEffect与memo一起使用(2', () => {
|
||||||
|
const compare = (prevProps, nextProps) => prevProps.num === nextProps.num;
|
||||||
|
const App = memo((props) => {
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`num effect [${props.num}]`);
|
||||||
|
return () => {
|
||||||
|
LogUtils.log(`num effect destroy ${props.num}`);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return <Text text={props.num} />;
|
||||||
|
}, compare)
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
0,
|
||||||
|
'callback effect'
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toEqual('0');
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['num effect [0]']);
|
||||||
|
|
||||||
|
// 不会重新渲染
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['callback effect']);
|
||||||
|
expect(container.textContent).toEqual('0');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
|
||||||
|
// 会重新渲染
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
1,
|
||||||
|
'callback effect',
|
||||||
|
// 执行异步,先清除旧的,再执行新的
|
||||||
|
'num effect destroy 0',
|
||||||
|
'num effect [1]'
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toEqual('1');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['callback effect', 'num effect destroy 1']);
|
||||||
|
expect(container.textContent).toEqual('');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('useEffect处理错误', () => {
|
||||||
|
const App = (props) => {
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log('throw Error');
|
||||||
|
throw new Error('mistake');
|
||||||
|
// eslint-disable-next-line no-unreachable
|
||||||
|
LogUtils.log(`Mount with [${props.num}]`);
|
||||||
|
return () => {
|
||||||
|
LogUtils.log(`Unmount with [${props.num}]`);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return <Text text={'Number: ' + props.num} />;
|
||||||
|
}
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={0} />, container, () =>
|
||||||
|
LogUtils.log('App callback effect'),
|
||||||
|
);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['Number: 0', 'App callback effect']);
|
||||||
|
expect(container.textContent).toEqual('Number: 0');
|
||||||
|
});
|
||||||
|
// 处理错误,不会向下执行LogUtils.log(`Mount with [${props.num}]`);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['throw Error']);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(null, container, () =>
|
||||||
|
LogUtils.log('App callback effect'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'App callback effect',
|
||||||
|
// 不会处理卸载部分 LogUtils.log(`Unmount with [${props.num}]`);
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toEqual('');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('卸载useEffect', () => {
|
||||||
|
const App = (props) => {
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`num effect [${props.num}]`);
|
||||||
|
return () => {
|
||||||
|
LogUtils.log(`num effect destroy ${props.num}`);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
if (props.num < 0) {
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`New num effect [${props.num}]`);
|
||||||
|
return () => {
|
||||||
|
LogUtils.log(`New num effect destroy ${props.num}`);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
return <Text text={`Number: ${props.num}`} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('num effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['Number: 0', 'num effect']);
|
||||||
|
expect(container.textContent).toBe('Number: 0');
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['num effect [0]']);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(null, container, () => LogUtils.log('num effect'));
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['num effect', 'num effect destroy 0']);
|
||||||
|
expect(container.textContent).toBe('');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('同步刷新不会导致effect执行', () => {
|
||||||
|
let setNum;
|
||||||
|
const App = () => {
|
||||||
|
const [num, _setNum] = useState(0);
|
||||||
|
setNum = _setNum;
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`num effect [${num}]`);
|
||||||
|
_setNum(1);
|
||||||
|
}, []);
|
||||||
|
return <Text text={`Number: ${num}`} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizonDOM.render(<App />, container, () => LogUtils.log('num effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['Number: 0', 'num effect']);
|
||||||
|
expect(container.textContent).toBe('Number: 0');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
// 模拟同步刷新
|
||||||
|
(function () {
|
||||||
|
setNum(2);
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['num effect [0]', 'Number: 1']);
|
||||||
|
expect(container.textContent).toBe('Number: 1');
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('当组件的更新方法在卸载函数中,组件更新不会告警', () => {
|
||||||
|
const App = () => {
|
||||||
|
LogUtils.log('useEffect');
|
||||||
|
const [num, setNum] = useState(0);
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log('effect');
|
||||||
|
return () => {
|
||||||
|
setNum(1);
|
||||||
|
LogUtils.log('effect destroy');
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App />, container, () => LogUtils.log('num effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['useEffect', 'num effect']);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['effect']);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(null, container);
|
||||||
|
});
|
||||||
|
// 不会处理setNum(1)
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['effect destroy']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('当组件的更新方法在卸载函数中,组件的子组件更新不会告警', () => {
|
||||||
|
const App = () => {
|
||||||
|
LogUtils.log('App');
|
||||||
|
const appRef = React.createRef(null);
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log('App effect');
|
||||||
|
return () => {
|
||||||
|
appRef.current(1);
|
||||||
|
LogUtils.log('App effect destroy');
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return <AppChild ref={appRef} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
let AppChild = (props, ref) => {
|
||||||
|
LogUtils.log('AppChild')
|
||||||
|
const [num, setNum] = useState(0);
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log('Child effect');
|
||||||
|
ref.current = setNum;
|
||||||
|
}, []);
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
AppChild = forwardRef(AppChild);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App />, container, () => LogUtils.log('num effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'App',
|
||||||
|
'AppChild',
|
||||||
|
'num effect'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['Child effect', 'App effect']);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(null, container);
|
||||||
|
});
|
||||||
|
// 销毁时执行appRef.current(1)不会报错
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['App effect destroy']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('当组件的更新方法在卸载函数中,组件的父组件更新不会告警', () => {
|
||||||
|
const App = () => {
|
||||||
|
LogUtils.log('App');
|
||||||
|
const [num, setNum] = useState(0);
|
||||||
|
return <AppChild num={num} setNum={setNum} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
let AppChild = (props) => {
|
||||||
|
LogUtils.log('AppChild')
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log('Child effect');
|
||||||
|
return () => {
|
||||||
|
LogUtils.log('Child effect destroy');
|
||||||
|
props.setNum(1);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return props.num;
|
||||||
|
}
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App />, container, () => LogUtils.log('num effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'App',
|
||||||
|
'AppChild',
|
||||||
|
'num effect'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['Child effect']);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(null, container);
|
||||||
|
});
|
||||||
|
// 销毁时执行 props.setNum(1);不会报错
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['Child effect destroy']);
|
||||||
|
});
|
||||||
|
})
|
|
@ -0,0 +1,91 @@
|
||||||
|
/* eslint-disable no-undef */
|
||||||
|
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||||
|
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||||
|
import * as LogUtils from '../../jest/logUtils';
|
||||||
|
import { act } from '../../jest/customMatcher';
|
||||||
|
import Text from '../../jest/Text';
|
||||||
|
|
||||||
|
describe('useImperativeHandle Hook Test', () => {
|
||||||
|
const {
|
||||||
|
useState,
|
||||||
|
useImperativeHandle,
|
||||||
|
forwardRef
|
||||||
|
} = React;
|
||||||
|
const { unmountComponentAtNode } = HorizonDOM;
|
||||||
|
|
||||||
|
it('测试useImperativeHandle', () => {
|
||||||
|
|
||||||
|
let App = (props, ref) => {
|
||||||
|
const [num, setNum] = useState(0);
|
||||||
|
useImperativeHandle(ref, () => ({ num, setNum }), []);
|
||||||
|
return <p>{num}</p>;
|
||||||
|
}
|
||||||
|
let App1 = (props, ref) => {
|
||||||
|
const [num1, setNum1] = useState(0);
|
||||||
|
useImperativeHandle(ref, () => ({ num1, setNum1 }), [num1]);
|
||||||
|
return <p>{num1}</p>;
|
||||||
|
}
|
||||||
|
|
||||||
|
App = forwardRef(App);
|
||||||
|
App1 = forwardRef(App1);
|
||||||
|
const counter = React.createRef(null);
|
||||||
|
const counter1 = React.createRef(null);
|
||||||
|
HorizonDOM.render(<App ref={counter} />, container);
|
||||||
|
expect(counter.current.num).toBe(0);
|
||||||
|
act(() => {
|
||||||
|
counter.current.setNum(1);
|
||||||
|
});
|
||||||
|
// useImperativeHandle的dep为[],所以不会变
|
||||||
|
expect(counter.current.num).toBe(0);
|
||||||
|
// 清空container
|
||||||
|
unmountComponentAtNode(container);
|
||||||
|
|
||||||
|
HorizonDOM.render(<App1 ref={counter1} />, container);
|
||||||
|
expect(counter1.current.num1).toBe(0);
|
||||||
|
act(() => {
|
||||||
|
counter1.current.setNum1(1);
|
||||||
|
});
|
||||||
|
// useImperativeHandle的dep为[num1],所以会变
|
||||||
|
expect(counter1.current.num1).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('useImperativeHandle没有配置dep时自动更新', () => {
|
||||||
|
|
||||||
|
let App = (props, ref) => {
|
||||||
|
const [num, setNum] = useState(0);
|
||||||
|
useImperativeHandle(ref, () => ({ num, setNum }));
|
||||||
|
return <Text text={num} />;
|
||||||
|
}
|
||||||
|
let App1 = (props, ref) => {
|
||||||
|
const [num1, setNum1] = useState(0);
|
||||||
|
useImperativeHandle(ref, () => ({ num1, setNum1 }), []);
|
||||||
|
return <Text text={num1} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
App = forwardRef(App);
|
||||||
|
App1 = forwardRef(App1);
|
||||||
|
const counter = React.createRef(null);
|
||||||
|
const counter1 = React.createRef(null);
|
||||||
|
HorizonDOM.render(<App ref={counter} />, container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([0]);
|
||||||
|
expect(counter.current.num).toBe(0);
|
||||||
|
act(() => {
|
||||||
|
counter.current.setNum(1);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([1]);
|
||||||
|
// useImperativeHandle没有配置的dep,所以会自动更新
|
||||||
|
expect(counter.current.num).toBe(1);
|
||||||
|
// 清空container
|
||||||
|
unmountComponentAtNode(container);
|
||||||
|
|
||||||
|
HorizonDOM.render(<App1 ref={counter1} />, container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([0]);
|
||||||
|
expect(counter1.current.num1).toBe(0);
|
||||||
|
act(() => {
|
||||||
|
counter1.current.setNum1(1);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([1]);
|
||||||
|
// useImperativeHandle的dep为[],所以不会变
|
||||||
|
expect(counter1.current.num1).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,116 @@
|
||||||
|
/* eslint-disable no-undef */
|
||||||
|
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||||
|
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||||
|
import * as LogUtils from '../../jest/logUtils';
|
||||||
|
import { act } from '../../jest/customMatcher';
|
||||||
|
import Text from '../../jest/Text';
|
||||||
|
|
||||||
|
describe('useLayoutEffect Hook Test', () => {
|
||||||
|
const {
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
useLayoutEffect
|
||||||
|
} = React;
|
||||||
|
|
||||||
|
it('简单使用useLayoutEffect', () => {
|
||||||
|
const App = () => {
|
||||||
|
const [num, setNum] = useState(0);
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
document.getElementById('p').style.display = num === 0 ? 'none' : 'inline';
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p style={{ display: 'block' }} id="p">{num}</p>
|
||||||
|
<button onClick={() => setNum(num + 1)} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
HorizonDOM.render(<App />, container);
|
||||||
|
expect(document.getElementById('p').style.display).toBe('none');
|
||||||
|
container.querySelector('button').click();
|
||||||
|
expect(container.querySelector('p').style.display).toBe('inline');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('useLayoutEffect的触发时序', () => {
|
||||||
|
const App = (props) => {
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
LogUtils.log('LayoutEffect');
|
||||||
|
});
|
||||||
|
return <Text text={props.num} />;
|
||||||
|
}
|
||||||
|
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('Sync effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
1,
|
||||||
|
// 同步在渲染之后
|
||||||
|
'LayoutEffect',
|
||||||
|
'Sync effect'
|
||||||
|
]);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||||
|
// 更新
|
||||||
|
HorizonDOM.render(<App num={2} />, container, () => LogUtils.log('Sync effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
2,
|
||||||
|
'LayoutEffect',
|
||||||
|
'Sync effect'
|
||||||
|
]);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('创建,销毁useLayoutEffect', () => {
|
||||||
|
const App = (props) => {
|
||||||
|
useEffect(() => {
|
||||||
|
LogUtils.log(`num effect [${props.num}]`);
|
||||||
|
return () => {
|
||||||
|
LogUtils.log('num effect destroy');
|
||||||
|
};
|
||||||
|
}, [props.num]);
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
LogUtils.log(`num Layouteffect [${props.num}]`);
|
||||||
|
return () => {
|
||||||
|
LogUtils.log(`num [${props.num}] Layouteffect destroy`);
|
||||||
|
};
|
||||||
|
}, [props.num]);
|
||||||
|
return <Text text={'num: ' + props.num} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
'num: 0',
|
||||||
|
'num Layouteffect [0]',
|
||||||
|
'callback effect'
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toBe('num: 0');
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
||||||
|
})
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
// 异步effect
|
||||||
|
'num effect [0]',
|
||||||
|
'num: 1',
|
||||||
|
// 旧Layouteffect销毁
|
||||||
|
'num [0] Layouteffect destroy',
|
||||||
|
// 新Layouteffect建立
|
||||||
|
'num Layouteffect [1]',
|
||||||
|
'callback effect',
|
||||||
|
// 异步旧的effect销毁
|
||||||
|
'num effect destroy',
|
||||||
|
// 异步新的effect建立
|
||||||
|
'num effect [1]'
|
||||||
|
]);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
||||||
|
})
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
// 同步Layouteffect销毁
|
||||||
|
'num [1] Layouteffect destroy',
|
||||||
|
'callback effect',
|
||||||
|
// 最后执行异步effect销毁
|
||||||
|
'num effect destroy',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,107 @@
|
||||||
|
/* eslint-disable no-undef */
|
||||||
|
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||||
|
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||||
|
import * as LogUtils from '../../jest/logUtils';
|
||||||
|
import Text from '../../jest/Text';
|
||||||
|
|
||||||
|
describe('useMemo Hook Test', () => {
|
||||||
|
const { useMemo, useState } = React;
|
||||||
|
|
||||||
|
it('测试useMemo', () => {
|
||||||
|
let setMemo;
|
||||||
|
const App = () => {
|
||||||
|
const [num, setNum] = useState(0);
|
||||||
|
const [memoDependent, _setMemo] = useState('App');
|
||||||
|
setMemo = _setMemo;
|
||||||
|
const text = useMemo(() => {
|
||||||
|
setNum(num + 1);
|
||||||
|
return memoDependent;
|
||||||
|
}, [memoDependent])
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>{text}</p>
|
||||||
|
<p id="p">{num}</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
HorizonDOM.render(<App words="App" />, container);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('App');
|
||||||
|
expect(container.querySelector('#p').innerHTML).toBe('1');
|
||||||
|
// 修改useMemo的依赖项,num会加一,text会改变。
|
||||||
|
setMemo('Apps')
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('Apps');
|
||||||
|
expect(container.querySelector('#p').innerHTML).toBe('2');
|
||||||
|
// useMemo的依赖项不变,num不会加一,text不会改变。
|
||||||
|
setMemo('Apps')
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('Apps');
|
||||||
|
expect(container.querySelector('#p').innerHTML).toBe('2');
|
||||||
|
// 修改useMemo的依赖项,num会加一,text会改变。
|
||||||
|
setMemo('App')
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('App');
|
||||||
|
expect(container.querySelector('#p').innerHTML).toBe('3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('触发useMemo', () => {
|
||||||
|
const App = (props) => {
|
||||||
|
const num = useMemo(() => {
|
||||||
|
LogUtils.log(props._num);
|
||||||
|
return props._num + 1;
|
||||||
|
}, [props._num]);
|
||||||
|
return <Text text={num} />;
|
||||||
|
}
|
||||||
|
HorizonDOM.render(<App _num={0} />, container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toBe('1');
|
||||||
|
|
||||||
|
HorizonDOM.render(<App _num={1} />, container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toBe('2');
|
||||||
|
|
||||||
|
HorizonDOM.render(<App _num={1} />, container);
|
||||||
|
// 不会触发useMemo
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([2]);
|
||||||
|
expect(container.textContent).toBe('2');
|
||||||
|
|
||||||
|
HorizonDOM.render(<App _num={2} />, container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
]);
|
||||||
|
expect(container.textContent).toBe('3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('输入不变,重新渲染也会触发useMemo', () => {
|
||||||
|
const App = (props) => {
|
||||||
|
const num = useMemo(props._num);
|
||||||
|
return <Text text={num} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const num1 = () => {
|
||||||
|
LogUtils.log('num 1');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const num2 = () => {
|
||||||
|
LogUtils.log('num 2');
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizonDOM.render(<App _num={num1} />, container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['num 1', 1]);
|
||||||
|
|
||||||
|
HorizonDOM.render(<App _num={num1} />, container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['num 1', 1]);
|
||||||
|
|
||||||
|
HorizonDOM.render(<App _num={num1} />, container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['num 1', 1]);
|
||||||
|
|
||||||
|
HorizonDOM.render(<App _num={num2} />, container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['num 2', 2]);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,61 @@
|
||||||
|
/* eslint-disable no-undef */
|
||||||
|
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||||
|
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||||
|
|
||||||
|
describe('useReducer Hook Test', () => {
|
||||||
|
const { useReducer } = React;
|
||||||
|
|
||||||
|
it('简单使用useReducer', () => {
|
||||||
|
const intlCar = { logo: '', price: 0 };
|
||||||
|
let dispatch;
|
||||||
|
const App = () => {
|
||||||
|
const carReducer = (state, action) => {
|
||||||
|
switch (action.logo) {
|
||||||
|
case 'ford':
|
||||||
|
return {
|
||||||
|
...intlCar,
|
||||||
|
logo: 'ford',
|
||||||
|
price: 76
|
||||||
|
};
|
||||||
|
case 'bmw':
|
||||||
|
return {
|
||||||
|
...intlCar,
|
||||||
|
logo: 'bmw',
|
||||||
|
price: 100
|
||||||
|
};
|
||||||
|
case 'benz':
|
||||||
|
return {
|
||||||
|
...intlCar,
|
||||||
|
logo: 'benz',
|
||||||
|
price: 80
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
...intlCar,
|
||||||
|
logo: 'audi',
|
||||||
|
price: 88
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const [car, carDispatch] = useReducer(carReducer, intlCar);
|
||||||
|
dispatch = carDispatch;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>{car.logo}</p>
|
||||||
|
<p id={'senP'}>{car.price}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
HorizonDOM.render(<App />, container);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('');
|
||||||
|
expect(container.querySelector('#senP').innerHTML).toBe('0');
|
||||||
|
// 触发bmw
|
||||||
|
dispatch({ logo: 'bmw' });
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('bmw');
|
||||||
|
expect(container.querySelector('#senP').innerHTML).toBe('100');
|
||||||
|
// 触发carReducer里的switch的default项
|
||||||
|
dispatch({ logo: 'wrong logo' });
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('audi');
|
||||||
|
expect(container.querySelector('#senP').innerHTML).toBe('88');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,58 @@
|
||||||
|
/* eslint-disable no-undef */
|
||||||
|
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||||
|
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||||
|
import * as LogUtils from '../../jest/logUtils';
|
||||||
|
import Text from '../../jest/Text';
|
||||||
|
|
||||||
|
describe('useRef Hook Test', () => {
|
||||||
|
const { useState, useRef } = React;
|
||||||
|
|
||||||
|
it('测试useRef', () => {
|
||||||
|
const App = () => {
|
||||||
|
const [num, setNum] = useState(1);
|
||||||
|
const ref = useRef();
|
||||||
|
if (!ref.current) {
|
||||||
|
ref.current = num;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>{num}</p>
|
||||||
|
<p id="sp">{ref.current}</p>
|
||||||
|
<button onClick={() => setNum(num + 1)} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
HorizonDOM.render(<App />, container);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||||
|
expect(container.querySelector('#sp').innerHTML).toBe('1');
|
||||||
|
// 点击按钮触发num加1,ref不变
|
||||||
|
container.querySelector('button').click();
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('2');
|
||||||
|
expect(container.querySelector('#sp').innerHTML).toBe('1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('更新current值时不会re-render', () => {
|
||||||
|
const App = () => {
|
||||||
|
const ref = useRef(1);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button onClick={() => {
|
||||||
|
ref.current += 1;
|
||||||
|
}}>
|
||||||
|
button
|
||||||
|
</button>
|
||||||
|
<Text text={ref.current} />;
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
HorizonDOM.render(<App />, container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([1]);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||||
|
// 点击按钮触发ref.current加1
|
||||||
|
container.querySelector('button').click();
|
||||||
|
// ref.current改变不会触发重新渲染
|
||||||
|
expect(LogUtils.getAndClear()).toEqual([]);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,30 +1,35 @@
|
||||||
import * as React from '../../../libs/horizon/src/external/Horizon';
|
/* eslint-disable no-undef */
|
||||||
import * as HorizonDOM from '../../../libs/horizon/src/dom/DOMExternal';
|
import * as React from '../../../../libs/horizon/src/external/Horizon';
|
||||||
import * as LogUtils from '../jest/logUtils';
|
import * as HorizonDOM from '../../../../libs/horizon/src/dom/DOMExternal';
|
||||||
|
import * as LogUtils from '../../jest/logUtils';
|
||||||
|
import { act } from '../../jest/customMatcher';
|
||||||
|
import Text from '../../jest/Text';
|
||||||
|
|
||||||
describe('useState Hook Test', () => {
|
describe('useState Hook Test', () => {
|
||||||
const { useState, forwardRef, useImperativeHandle, memo } = React;
|
const {
|
||||||
const { unmountComponentAtNode } = HorizonDOM;
|
useState,
|
||||||
let container = null;
|
forwardRef,
|
||||||
beforeEach(() => {
|
useImperativeHandle,
|
||||||
// 创建一个 DOM 元素作为渲染目标
|
memo
|
||||||
container = document.createElement('div');
|
} = React;
|
||||||
document.body.appendChild(container);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
it('简单使用useState', () => {
|
||||||
// 退出时进行清理
|
const App = () => {
|
||||||
unmountComponentAtNode(container);
|
const [num, setNum] = useState(0);
|
||||||
container.remove();
|
return (
|
||||||
container = null;
|
<>
|
||||||
LogUtils.clear();
|
<p>{num}</p>
|
||||||
|
<button onClick={() => setNum(num + 1)} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
HorizonDOM.render(<App />, container);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('0');
|
||||||
|
// 点击按钮触发num加1
|
||||||
|
container.querySelector('button').click();
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||||
});
|
});
|
||||||
|
|
||||||
const Text = (props) => {
|
|
||||||
LogUtils.log(props.text);
|
|
||||||
return <p>{props.text}</p>;
|
|
||||||
}
|
|
||||||
|
|
||||||
it('多个useState', () => {
|
it('多个useState', () => {
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [num, setNum] = useState(0);
|
const [num, setNum] = useState(0);
|
||||||
|
@ -126,4 +131,42 @@ describe('useState Hook Test', () => {
|
||||||
expect(LogUtils.getAndClear()).toEqual([1]);
|
expect(LogUtils.getAndClear()).toEqual([1]);
|
||||||
expect(container.querySelector('p').innerHTML).toBe('1');
|
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('卸载useState', () => {
|
||||||
|
// let updateA;
|
||||||
|
let setNum;
|
||||||
|
let setCount;
|
||||||
|
|
||||||
|
const App = (props) => {
|
||||||
|
const [num, setNum_1] = useState(0);
|
||||||
|
setNum = setNum_1;
|
||||||
|
|
||||||
|
let count;
|
||||||
|
if (props.hasCount) {
|
||||||
|
const [count_1, setCount_1] = useState(0);
|
||||||
|
count = count_1;
|
||||||
|
setCount = setCount_1;
|
||||||
|
} else {
|
||||||
|
count = 'null';
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Text text={`Number: ${num}, Count: ${count}`} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizonDOM.render(<App hasCount={true} />, container);
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['Number: 0, Count: 0']);
|
||||||
|
expect(container.textContent).toBe('Number: 0, Count: 0');
|
||||||
|
act(() => {
|
||||||
|
setNum(1);
|
||||||
|
setCount(2);
|
||||||
|
});
|
||||||
|
expect(LogUtils.getAndClear()).toEqual(['Number: 1, Count: 2']);
|
||||||
|
expect(container.textContent).toBe('Number: 1, Count: 2');
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
HorizonDOM.render(<App hasCount={false} />, container);
|
||||||
|
}).toThrow(
|
||||||
|
'Hooks are less than expected, please check whether the hook is written in the condition.',
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
|
@ -1,312 +0,0 @@
|
||||||
import * as React from '../../../libs/horizon/src/external/Horizon';
|
|
||||||
import * as HorizonDOM from '../../../libs/horizon/src/dom/DOMExternal';
|
|
||||||
import { act } from '../jest/customMatcher';
|
|
||||||
|
|
||||||
|
|
||||||
describe('Hook Test', () => {
|
|
||||||
const {
|
|
||||||
useState,
|
|
||||||
useReducer,
|
|
||||||
useEffect,
|
|
||||||
useLayoutEffect,
|
|
||||||
useContext,
|
|
||||||
useMemo,
|
|
||||||
useCallback,
|
|
||||||
useRef,
|
|
||||||
useImperativeHandle,
|
|
||||||
forwardRef
|
|
||||||
} = React;
|
|
||||||
const { unmountComponentAtNode } = HorizonDOM;
|
|
||||||
let container = null;
|
|
||||||
beforeEach(() => {
|
|
||||||
// 创建一个 DOM 元素作为渲染目标
|
|
||||||
container = document.createElement('div');
|
|
||||||
document.body.appendChild(container);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
// 退出时进行清理
|
|
||||||
unmountComponentAtNode(container);
|
|
||||||
container.remove();
|
|
||||||
container = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('简单使用useState', () => {
|
|
||||||
const App = () => {
|
|
||||||
const [num, setNum] = useState(0);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p>{num}</p>
|
|
||||||
<button onClick={() => setNum(num + 1)} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
HorizonDOM.render(<App />, container);
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('0');
|
|
||||||
// 点击按钮触发num加1
|
|
||||||
container.querySelector('button').click();
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('1');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('简单使用useEffect', () => {
|
|
||||||
const App = () => {
|
|
||||||
const [num, setNum] = useState(0);
|
|
||||||
useEffect(() => {
|
|
||||||
document.getElementById('p').style.display = num === 0 ? 'none' : 'inline';
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p style={{ display: 'block' }} id="p">{num}</p>
|
|
||||||
<button onClick={() => setNum(num + 1)} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
HorizonDOM.render(<App />, container);
|
|
||||||
expect(document.getElementById('p').style.display).toBe('block');
|
|
||||||
// 点击按钮触发num加1
|
|
||||||
container.querySelector('button').click();
|
|
||||||
expect(document.getElementById('p').style.display).toBe('none');
|
|
||||||
container.querySelector('button').click();
|
|
||||||
expect(container.querySelector('p').style.display).toBe('inline');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('简单使用useLayoutEffect', () => {
|
|
||||||
const App = () => {
|
|
||||||
const [num, setNum] = useState(0);
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
document.getElementById('p').style.display = num === 0 ? 'none' : 'inline';
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p style={{ display: 'block' }} id="p">{num}</p>
|
|
||||||
<button onClick={() => setNum(num + 1)} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
HorizonDOM.render(<App />, container);
|
|
||||||
expect(document.getElementById('p').style.display).toBe('none');
|
|
||||||
container.querySelector('button').click();
|
|
||||||
expect(container.querySelector('p').style.display).toBe('inline');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('简单使用useContext', () => {
|
|
||||||
const LanguageTypes = {
|
|
||||||
JAVA: 'Java',
|
|
||||||
JAVASCRIPT: 'JavaScript',
|
|
||||||
};
|
|
||||||
const defaultValue = { type: LanguageTypes.JAVASCRIPT };
|
|
||||||
const SystemLanguageContext = React.createContext(defaultValue);
|
|
||||||
|
|
||||||
const SystemLanguageProvider = ({ type, children }) => {
|
|
||||||
return (
|
|
||||||
<SystemLanguageContext.Provider value={{ type }}>
|
|
||||||
{children}
|
|
||||||
</SystemLanguageContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const TestFunction = () => {
|
|
||||||
const context = useContext(SystemLanguageContext);
|
|
||||||
return <p id="p">{context.type}</p>;
|
|
||||||
}
|
|
||||||
let setValue;
|
|
||||||
const App = () => {
|
|
||||||
const [value, _setValue] = useState(LanguageTypes.JAVA);
|
|
||||||
setValue = _setValue;
|
|
||||||
return (
|
|
||||||
<div className="App">
|
|
||||||
<SystemLanguageProvider type={value}>
|
|
||||||
<TestFunction />
|
|
||||||
</SystemLanguageProvider>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
HorizonDOM.render(<TestFunction />, container);
|
|
||||||
// 测试当Provider未提供时,获取到的默认值'JavaScript'。
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('JavaScript');
|
|
||||||
unmountComponentAtNode(container);
|
|
||||||
HorizonDOM.render(<App />, container);
|
|
||||||
// 测试当Provider提供时,可以获取到Provider的值'Java'。
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('Java');
|
|
||||||
// 测试当Provider改变时,可以获取到最新Provider的值。
|
|
||||||
act(() => setValue(LanguageTypes.JAVASCRIPT));
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('JavaScript');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('简单使用useReducer', () => {
|
|
||||||
const intlCar = { logo: '', price: 0 };
|
|
||||||
let dispatch;
|
|
||||||
const App = () => {
|
|
||||||
const carReducer = (state, action) => {
|
|
||||||
switch (action.logo) {
|
|
||||||
case 'ford':
|
|
||||||
return {
|
|
||||||
...intlCar,
|
|
||||||
logo: 'ford',
|
|
||||||
price: 76
|
|
||||||
};
|
|
||||||
case 'bmw':
|
|
||||||
return {
|
|
||||||
...intlCar,
|
|
||||||
logo: 'bmw',
|
|
||||||
price: 100
|
|
||||||
};
|
|
||||||
case 'benz':
|
|
||||||
return {
|
|
||||||
...intlCar,
|
|
||||||
logo: 'benz',
|
|
||||||
price: 80
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return {
|
|
||||||
...intlCar,
|
|
||||||
logo: 'audi',
|
|
||||||
price: 88
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const [car, carDispatch] = useReducer(carReducer, intlCar);
|
|
||||||
dispatch = carDispatch;
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<p>{car.logo}</p>
|
|
||||||
<p id={'senP'}>{car.price}</p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
HorizonDOM.render(<App />, container);
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('');
|
|
||||||
expect(container.querySelector('#senP').innerHTML).toBe('0');
|
|
||||||
// 触发bmw
|
|
||||||
dispatch({ logo: 'bmw' });
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('bmw');
|
|
||||||
expect(container.querySelector('#senP').innerHTML).toBe('100');
|
|
||||||
// 触发carReducer里的switch的default项
|
|
||||||
dispatch({ logo: 'wrong logo' });
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('audi');
|
|
||||||
expect(container.querySelector('#senP').innerHTML).toBe('88');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('测试useMemo', () => {
|
|
||||||
let setMemo;
|
|
||||||
const App = () => {
|
|
||||||
const [num, setNum] = useState(0);
|
|
||||||
const [memoDependent, _setMemo] = useState('App');
|
|
||||||
setMemo = _setMemo;
|
|
||||||
const text = useMemo(() => {
|
|
||||||
setNum(num + 1);
|
|
||||||
return memoDependent;
|
|
||||||
}, [memoDependent])
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p>{text}</p>
|
|
||||||
<p id="p">{num}</p>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
HorizonDOM.render(<App words="App" />, container);
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('App');
|
|
||||||
expect(container.querySelector('#p').innerHTML).toBe('1');
|
|
||||||
// 修改useMemo的依赖项,num会加一,text会改变。
|
|
||||||
setMemo('Apps')
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('Apps');
|
|
||||||
expect(container.querySelector('#p').innerHTML).toBe('2');
|
|
||||||
// useMemo的依赖项不变,num不会加一,text不会改变。
|
|
||||||
setMemo('Apps')
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('Apps');
|
|
||||||
expect(container.querySelector('#p').innerHTML).toBe('2');
|
|
||||||
// 修改useMemo的依赖项,num会加一,text会改变。
|
|
||||||
setMemo('App')
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('App');
|
|
||||||
expect(container.querySelector('#p').innerHTML).toBe('3');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('测试useCallback', () => {
|
|
||||||
const App = (props) => {
|
|
||||||
const [num, setNum] = useState(0);
|
|
||||||
const NumUseCallback = useCallback(() => {
|
|
||||||
setNum(num + props.text)
|
|
||||||
}, [props]);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p>{num}</p>
|
|
||||||
<button onClick={NumUseCallback} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
HorizonDOM.render(<App text={1} />, container);
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('0');
|
|
||||||
// 点击按钮触发num加1
|
|
||||||
container.querySelector('button').click();
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('1');
|
|
||||||
// 再次点击,依赖项没变,num不增加
|
|
||||||
container.querySelector('button').click();
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('1');
|
|
||||||
|
|
||||||
HorizonDOM.render(<App text={2} />, container);
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('1');
|
|
||||||
// 依赖项有变化,点击按钮num增加
|
|
||||||
container.querySelector('button').click();
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('3');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('测试useRef', () => {
|
|
||||||
const App = () => {
|
|
||||||
const [num, setNum] = useState(1);
|
|
||||||
const ref = useRef();
|
|
||||||
if (!ref.current) {
|
|
||||||
ref.current = num;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p>{num}</p>
|
|
||||||
<p id="sp">{ref.current}</p>
|
|
||||||
<button onClick={() => setNum(num + 1)} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
HorizonDOM.render(<App />, container);
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('1');
|
|
||||||
expect(container.querySelector('#sp').innerHTML).toBe('1');
|
|
||||||
// 点击按钮触发num加1,ref不变
|
|
||||||
container.querySelector('button').click();
|
|
||||||
expect(container.querySelector('p').innerHTML).toBe('2');
|
|
||||||
expect(container.querySelector('#sp').innerHTML).toBe('1');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('测试useImperativeHandle', () => {
|
|
||||||
|
|
||||||
let App = (props, ref) => {
|
|
||||||
const [num, setNum] = useState(0);
|
|
||||||
useImperativeHandle(ref, () => ({num, setNum}), []);
|
|
||||||
return <p>{num}</p>;
|
|
||||||
}
|
|
||||||
let App1 = (props, ref) => {
|
|
||||||
const [num1, setNum1] = useState(0);
|
|
||||||
useImperativeHandle(ref, () => ({num1, setNum1}), [num1]);
|
|
||||||
return <p>{num1}</p>;
|
|
||||||
}
|
|
||||||
|
|
||||||
App = forwardRef(App);
|
|
||||||
App1 = forwardRef(App1);
|
|
||||||
const counter = React.createRef(null);
|
|
||||||
const counter1 = React.createRef(null);
|
|
||||||
HorizonDOM.render(<App ref={counter} />, container);
|
|
||||||
expect(counter.current.num).toBe(0);
|
|
||||||
act(() => {
|
|
||||||
counter.current.setNum(1);
|
|
||||||
});
|
|
||||||
// useImperativeHandle的dep为[],所以不会变
|
|
||||||
expect(counter.current.num).toBe(0);
|
|
||||||
// 清空container
|
|
||||||
unmountComponentAtNode(container);
|
|
||||||
|
|
||||||
HorizonDOM.render(<App1 ref={counter1} />, container);
|
|
||||||
expect(counter1.current.num1).toBe(0);
|
|
||||||
act(() => {
|
|
||||||
counter1.current.setNum1(1);
|
|
||||||
});
|
|
||||||
// useImperativeHandle的dep为[num1],所以会变
|
|
||||||
expect(counter1.current.num1).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,442 +0,0 @@
|
||||||
import * as React from '../../../libs/horizon/src/external/Horizon';
|
|
||||||
import * as HorizonDOM from '../../../libs/horizon/src/dom/DOMExternal';
|
|
||||||
import * as LogUtils from '../jest/logUtils';
|
|
||||||
import { act } from '../jest/customMatcher';
|
|
||||||
|
|
||||||
describe('useEffect Hook Test', () => {
|
|
||||||
const { useEffect, useLayoutEffect, useState, memo } = React;
|
|
||||||
const { unmountComponentAtNode } = HorizonDOM;
|
|
||||||
let container = null;
|
|
||||||
beforeEach(() => {
|
|
||||||
LogUtils.clear();
|
|
||||||
// 创建一个 DOM 元素作为渲染目标
|
|
||||||
container = document.createElement('div');
|
|
||||||
document.body.appendChild(container);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
// 退出时进行清理
|
|
||||||
unmountComponentAtNode(container);
|
|
||||||
container.remove();
|
|
||||||
container = null;
|
|
||||||
LogUtils.clear();
|
|
||||||
});
|
|
||||||
|
|
||||||
const Text = (props) => {
|
|
||||||
LogUtils.log(props.text);
|
|
||||||
return <p>{props.text}</p>;
|
|
||||||
}
|
|
||||||
|
|
||||||
it('兄弟节点被删除,useEffect依然正常', () => {
|
|
||||||
const App = () => {
|
|
||||||
return <Text text="App" />;
|
|
||||||
}
|
|
||||||
const NewApp = () => {
|
|
||||||
useEffect(() => {
|
|
||||||
LogUtils.log(`NewApp effect`);
|
|
||||||
}, []);
|
|
||||||
return <Text text="NewApp" />;
|
|
||||||
}
|
|
||||||
const na = <NewApp />;
|
|
||||||
// <App />必须设置key值,否则在diff的时候na会被视为不同组件
|
|
||||||
HorizonDOM.render([<App key="app" />, na], container);
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'App',
|
|
||||||
'NewApp'
|
|
||||||
]);
|
|
||||||
expect(container.textContent).toBe('AppNewApp');
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([]);
|
|
||||||
// 在执行新的render前,会执行完上一次render的useEffect,所以LogUtils会加入'NewApp effect'。
|
|
||||||
HorizonDOM.render([na], container);
|
|
||||||
expect(LogUtils.getAndClear()).toEqual(['NewApp effect']);
|
|
||||||
expect(container.textContent).toBe('NewApp');
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('兄弟节点更新,useEffect依然正常', () => {
|
|
||||||
const App = () => {
|
|
||||||
const [num, setNum] = useState(0);
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
if (num === 0) {
|
|
||||||
setNum(1);
|
|
||||||
}
|
|
||||||
LogUtils.log('App Layout effect ' + num);
|
|
||||||
});
|
|
||||||
return <Text text="App" />;
|
|
||||||
}
|
|
||||||
const NewApp = () => {
|
|
||||||
useEffect(() => {
|
|
||||||
LogUtils.log(`NewApp effect`);
|
|
||||||
}, []);
|
|
||||||
return <Text text="NewApp" />;
|
|
||||||
}
|
|
||||||
// <App />必须设置key值,否则在diff的时候na会被视为不同组件
|
|
||||||
HorizonDOM.render([<App key="app" />, <NewApp />], container);
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'App',
|
|
||||||
'NewApp',
|
|
||||||
'App Layout effect 0',
|
|
||||||
// 在App更新前,会执行完NewApp的useEffect
|
|
||||||
'NewApp effect',
|
|
||||||
'App',
|
|
||||||
'App Layout effect 1',
|
|
||||||
]);
|
|
||||||
expect(container.textContent).toBe('AppNewApp');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('兄弟节点执行新的挂载动作,useEffect依然正常', () => {
|
|
||||||
const newContainer = document.createElement('div');
|
|
||||||
const App = () => {
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
LogUtils.log('App Layout effect');
|
|
||||||
HorizonDOM.render(<Text text="NewContainer" />, newContainer);
|
|
||||||
});
|
|
||||||
return <Text text="App" />;
|
|
||||||
}
|
|
||||||
const NewApp = () => {
|
|
||||||
useEffect(() => {
|
|
||||||
LogUtils.log(`NewApp effect`);
|
|
||||||
}, []);
|
|
||||||
return <Text text="NewApp" />;
|
|
||||||
}
|
|
||||||
// <App />必须设置key值,否则在diff的时候na会被视为不同组件
|
|
||||||
HorizonDOM.render([<App key="app" />, <NewApp />], container);
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'App',
|
|
||||||
'NewApp',
|
|
||||||
'App Layout effect',
|
|
||||||
// 在执行useLayoutEffectApp的render前,会执行完NewApp的useEffect
|
|
||||||
'NewApp effect',
|
|
||||||
'NewContainer',
|
|
||||||
]);
|
|
||||||
expect(container.textContent).toBe('AppNewApp');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('执行新render的useEffect前会先执行旧render的useEffect', () => {
|
|
||||||
const App = (props) => {
|
|
||||||
useEffect(() => {
|
|
||||||
LogUtils.log(`First effect [${props.num}]`);
|
|
||||||
});
|
|
||||||
return <Text text={'num: ' + props.num} />;
|
|
||||||
}
|
|
||||||
act(() => {
|
|
||||||
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual(['num: 0', 'callback effect']);
|
|
||||||
expect(container.textContent).toEqual('num: 0');
|
|
||||||
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
|
||||||
// 执行新的render前,会执行旧render的useEffect,所以会添加'First effect [0]'
|
|
||||||
expect(LogUtils.getAndClear()).toEqual(['First effect [0]', 'num: 1', 'callback effect']);
|
|
||||||
expect(container.textContent).toEqual('num: 1');
|
|
||||||
})
|
|
||||||
// 最后在act执行完后会执行新render的useEffect
|
|
||||||
expect(LogUtils.getAndClear()).toEqual(['First effect [1]']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('混合使用useEffect', () => {
|
|
||||||
const App = (props) => {
|
|
||||||
useEffect(() => {
|
|
||||||
LogUtils.log(`First effect [${props.num}]`);
|
|
||||||
});
|
|
||||||
useEffect(() => {
|
|
||||||
LogUtils.log(`Second effect [${props.num}]`);
|
|
||||||
});
|
|
||||||
return <Text text={'num: ' + props.num} />;
|
|
||||||
}
|
|
||||||
act(() => {
|
|
||||||
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual(['num: 0', 'callback effect']);
|
|
||||||
expect(container.textContent).toEqual('num: 0');
|
|
||||||
})
|
|
||||||
expect(LogUtils.getAndClear()).toEqual(['First effect [0]', 'Second effect [0]']);
|
|
||||||
act(() => {
|
|
||||||
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual(['num: 1', 'callback effect']);
|
|
||||||
expect(container.textContent).toEqual('num: 1');
|
|
||||||
})
|
|
||||||
expect(LogUtils.getAndClear()).toEqual(['First effect [1]', 'Second effect [1]']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('创建,销毁useEffect', () => {
|
|
||||||
const App = (props) => {
|
|
||||||
useEffect(() => {
|
|
||||||
LogUtils.log(`num effect [${props.num}]`);
|
|
||||||
return () => {
|
|
||||||
LogUtils.log('num effect destroy');
|
|
||||||
};
|
|
||||||
}, [props.num]);
|
|
||||||
useEffect(() => {
|
|
||||||
LogUtils.log(`word effect [${props.word}]`);
|
|
||||||
return () => {
|
|
||||||
LogUtils.log('word effect destroy');
|
|
||||||
};
|
|
||||||
}, [props.word]);
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
LogUtils.log(`num Layouteffect [${props.num}]`);
|
|
||||||
return () => {
|
|
||||||
LogUtils.log('num Layouteffect destroy');
|
|
||||||
};
|
|
||||||
}, [props.num]);
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
LogUtils.log(`word Layouteffect [${props.word}]`);
|
|
||||||
return () => {
|
|
||||||
LogUtils.log('word Layouteffect destroy');
|
|
||||||
};
|
|
||||||
}, [props.word]);
|
|
||||||
return <Text text={'num: ' + props.num + ',word: ' + props.word} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
HorizonDOM.render(<App num={0} word={'App'} />, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num: 0,word: App',
|
|
||||||
'num Layouteffect [0]',
|
|
||||||
'word Layouteffect [App]',
|
|
||||||
'callback effect'
|
|
||||||
]);
|
|
||||||
})
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num effect [0]',
|
|
||||||
'word effect [App]',
|
|
||||||
]);
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
// 此时word改变,num不变
|
|
||||||
HorizonDOM.render(<App num={0} word={'React'} />, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num: 0,word: React',
|
|
||||||
'word Layouteffect destroy',
|
|
||||||
'word Layouteffect [React]',
|
|
||||||
'callback effect'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'word effect destroy',
|
|
||||||
'word effect [React]',
|
|
||||||
]);
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
// 此时num和word的所有effect都销毁
|
|
||||||
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num Layouteffect destroy',
|
|
||||||
'word Layouteffect destroy',
|
|
||||||
'callback effect'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num effect destroy',
|
|
||||||
'word effect destroy',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('销毁不含依赖数组的useEffect', () => {
|
|
||||||
const App = (props) => {
|
|
||||||
useEffect(() => {
|
|
||||||
LogUtils.log(`num effect [${props.num}]`);
|
|
||||||
return () => {
|
|
||||||
LogUtils.log('num effect destroy');
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return <Text text={'num: ' + props.num} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num: 0',
|
|
||||||
'callback effect'
|
|
||||||
]);
|
|
||||||
expect(container.textContent).toEqual('num: 0');
|
|
||||||
})
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num effect [0]',
|
|
||||||
]);
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num: 1',
|
|
||||||
'callback effect'
|
|
||||||
]);
|
|
||||||
expect(container.textContent).toEqual('num: 1');
|
|
||||||
});
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num effect destroy',
|
|
||||||
'num effect [1]',
|
|
||||||
]);
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'callback effect'
|
|
||||||
]);
|
|
||||||
expect(container.textContent).toEqual('');
|
|
||||||
});
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num effect destroy'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('销毁依赖空数组的useEffect', () => {
|
|
||||||
const App = (props) => {
|
|
||||||
useEffect(() => {
|
|
||||||
LogUtils.log(`num effect [${props.num}]`);
|
|
||||||
return () => {
|
|
||||||
LogUtils.log('num effect destroy');
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
return <Text text={'num: ' + props.num} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num: 0',
|
|
||||||
'callback effect'
|
|
||||||
]);
|
|
||||||
expect(container.textContent).toEqual('num: 0');
|
|
||||||
})
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num effect [0]',
|
|
||||||
]);
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
HorizonDOM.render(<App num={1} />, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num: 1',
|
|
||||||
'callback effect'
|
|
||||||
]);
|
|
||||||
expect(container.textContent).toEqual('num: 1');
|
|
||||||
});
|
|
||||||
// 没有执行useEffect
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([]);
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'callback effect'
|
|
||||||
]);
|
|
||||||
expect(container.textContent).toEqual('');
|
|
||||||
});
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num effect destroy'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('useEffect里使用useState(1', () => {
|
|
||||||
let setNum;
|
|
||||||
const App = () => {
|
|
||||||
const [num, _setNum] = React.useState(0);
|
|
||||||
useEffect(() => {
|
|
||||||
LogUtils.log(`num effect [${num}]`);
|
|
||||||
setNum = () => _setNum(1);
|
|
||||||
}, [num]);
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
LogUtils.log(`num Layouteffect [${num}]`);
|
|
||||||
return () => {
|
|
||||||
LogUtils.log('num Layouteffect destroy');
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
return <Text text={'num: ' + num} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
HorizonDOM.render(<App num={0} />, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num: 0',
|
|
||||||
'num Layouteffect [0]',
|
|
||||||
'callback effect'
|
|
||||||
]);
|
|
||||||
})
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num effect [0]',
|
|
||||||
]);
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
setNum();
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num: 1'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
expect(LogUtils.getAndClear()).toEqual(['num effect [1]']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('useEffect里使用useState(2', () => {
|
|
||||||
let setNum;
|
|
||||||
const App = () => {
|
|
||||||
const [num, _setNum] = useState(0);
|
|
||||||
setNum = _setNum;
|
|
||||||
useEffect(() => {
|
|
||||||
LogUtils.log(`App effect`);
|
|
||||||
setNum(1);
|
|
||||||
}, []);
|
|
||||||
return <Text text={'Num: ' + num} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
HorizonDOM.render(<App />, container, () => LogUtils.log('Sync effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual(['Num: 0', 'Sync effect']);
|
|
||||||
expect(container.textContent).toEqual('Num: 0');
|
|
||||||
act(() => {
|
|
||||||
setNum(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 虽然执行了setNum(2),但执行到setNum(1),所以最终num为1
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'App effect',
|
|
||||||
'Num: 1',
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(container.textContent).toEqual('Num: 1');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('useEffect与memo一起使用', () => {
|
|
||||||
let setNum;
|
|
||||||
const App = memo(() => {
|
|
||||||
const [num, _setNum] = useState(0);
|
|
||||||
setNum = _setNum;
|
|
||||||
useEffect(() => {
|
|
||||||
LogUtils.log(`num effect [${num}]`);
|
|
||||||
return () => {
|
|
||||||
LogUtils.log(`num effect destroy ${num}`);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return <Text text={num} />;
|
|
||||||
})
|
|
||||||
act(() => {
|
|
||||||
HorizonDOM.render(<App />, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
0,
|
|
||||||
'callback effect'
|
|
||||||
]);
|
|
||||||
expect(container.textContent).toEqual('0');
|
|
||||||
});
|
|
||||||
expect(LogUtils.getAndClear()).toEqual(['num effect [0]']);
|
|
||||||
|
|
||||||
// 不会重新渲染
|
|
||||||
act(() => {
|
|
||||||
HorizonDOM.render(<App />, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual(['callback effect']);
|
|
||||||
expect(container.textContent).toEqual('0');
|
|
||||||
});
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([]);
|
|
||||||
|
|
||||||
// 会重新渲染
|
|
||||||
act(() => {
|
|
||||||
setNum(1);
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([1]);
|
|
||||||
expect(container.textContent).toEqual('1');
|
|
||||||
});
|
|
||||||
expect(LogUtils.getAndClear()).toEqual([
|
|
||||||
'num effect destroy 0',
|
|
||||||
'num effect [1]'
|
|
||||||
]);
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
HorizonDOM.render(null, container, () => LogUtils.log('callback effect'));
|
|
||||||
expect(LogUtils.getAndClear()).toEqual(['callback effect']);
|
|
||||||
expect(container.textContent).toEqual('');
|
|
||||||
});
|
|
||||||
expect(LogUtils.getAndClear()).toEqual(['num effect destroy 1']);
|
|
||||||
});
|
|
||||||
|
|
||||||
})
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import * as React from '../../../libs/horizon/src/external/Horizon';
|
||||||
|
import * as LogUtils from '../jest/logUtils';
|
||||||
|
|
||||||
|
const Text = (props) => {
|
||||||
|
LogUtils.log(props.text);
|
||||||
|
return <p>{props.text}</p>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Text;
|
|
@ -1,5 +1,6 @@
|
||||||
import { runAsyncEffects } from '../../../libs/horizon/src/renderer/submit/HookEffectHandler'
|
import { runAsyncEffects } from '../../../libs/horizon/src/renderer/submit/HookEffectHandler';
|
||||||
import { syncUpdates } from '../../../libs/horizon/src/renderer/TreeBuilder'
|
import { callRenderQueueImmediate } from '../../../libs/horizon/src/renderer/taskExecutor/RenderQueue';
|
||||||
|
import { asyncUpdates } from '../../../libs/horizon/src/renderer/TreeBuilder';
|
||||||
|
|
||||||
function runAssertion(fn) {
|
function runAssertion(fn) {
|
||||||
try {
|
try {
|
||||||
|
@ -21,7 +22,8 @@ function toMatchValue(LogUtils, expectedValues) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const act = (fun) => {
|
const act = (fun) => {
|
||||||
syncUpdates(fun);
|
asyncUpdates(fun);
|
||||||
|
callRenderQueueImmediate();
|
||||||
runAsyncEffects();
|
runAsyncEffects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
global.MessageChannel = function MessageChannel() {
|
||||||
|
this.port1 = {};
|
||||||
|
this.port2 = {
|
||||||
|
postMessage() { }
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,10 +1,21 @@
|
||||||
|
import { unmountComponentAtNode } from '../../../libs/horizon/src/dom/DOMExternal';
|
||||||
|
import * as LogUtils from '../jest/logUtils';
|
||||||
|
|
||||||
global.isDev = process.env.NODE_ENV === 'development';
|
global.isDev = process.env.NODE_ENV === 'development';
|
||||||
global.MessageChannel = function MessageChannel() {
|
global.container = null;
|
||||||
this.port1 = {};
|
global.beforeEach(() => {
|
||||||
this.port2 = {
|
LogUtils.clear();
|
||||||
postMessage() { }
|
// 创建一个 DOM 元素作为渲染目标
|
||||||
};
|
global.container = document.createElement('div');
|
||||||
};
|
document.body.appendChild(global.container);
|
||||||
|
});
|
||||||
|
|
||||||
|
global.afterEach(() => {
|
||||||
|
unmountComponentAtNode(global.container);
|
||||||
|
global.container.remove();
|
||||||
|
global.container = null;
|
||||||
|
LogUtils.clear();
|
||||||
|
});
|
||||||
|
|
||||||
// 使Jest感知自定义匹配器
|
// 使Jest感知自定义匹配器
|
||||||
expect.extend({
|
expect.extend({
|
||||||
|
|
Loading…
Reference in New Issue