Match-id-fa76e4980e36055d166fbde807cfd760cfa46a3d
This commit is contained in:
parent
c2f6ad0265
commit
8d516d8999
|
@ -2,7 +2,7 @@ module.exports = {
|
||||||
presets: [
|
presets: [
|
||||||
'@babel/react',
|
'@babel/react',
|
||||||
'@babel/preset-typescript',
|
'@babel/preset-typescript',
|
||||||
'@babel/preset-env'
|
['@babel/preset-env', { targets: { node: 'current' } }]
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
['@babel/plugin-proposal-class-properties', { loose: true }],
|
['@babel/plugin-proposal-class-properties', { loose: true }],
|
||||||
|
|
|
@ -12,7 +12,7 @@ module.exports = {
|
||||||
// browser: false,
|
// browser: false,
|
||||||
|
|
||||||
// The directory where Jest should store its cached dependency information
|
// The directory where Jest should store its cached dependency information
|
||||||
// cacheDirectory: "C:\\Users\\j30009756\\AppData\\Local\\Temp\\jest",
|
// cacheDirectory: "",
|
||||||
|
|
||||||
// Automatically clear mock calls and instances between every test
|
// Automatically clear mock calls and instances between every test
|
||||||
// clearMocks: false,
|
// clearMocks: false,
|
||||||
|
@ -58,15 +58,15 @@ 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',
|
// 'isDev': process.env.NODE_ENV === 'development',
|
||||||
'MessageChannel': function MessageChannel() {
|
// 'MessageChannel': function MessageChannel() {
|
||||||
this.port1 = {};
|
// this.port1 = {};
|
||||||
this.port2 = {
|
// this.port2 = {
|
||||||
postMessage() {}
|
// 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%",
|
||||||
|
@ -78,12 +78,8 @@ module.exports = {
|
||||||
|
|
||||||
// An array of file extensions your modules use
|
// An array of file extensions your modules use
|
||||||
// moduleFileExtensions: [
|
// moduleFileExtensions: [
|
||||||
// "js",
|
// 'js',
|
||||||
// "json",
|
// 'ts'
|
||||||
// "jsx",
|
|
||||||
// "ts",
|
|
||||||
// "tsx",
|
|
||||||
// "node"
|
|
||||||
// ],
|
// ],
|
||||||
|
|
||||||
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
||||||
|
@ -120,21 +116,21 @@ module.exports = {
|
||||||
// restoreMocks: false,
|
// restoreMocks: false,
|
||||||
|
|
||||||
// The root directory that Jest should scan for tests and modules within
|
// The root directory that Jest should scan for tests and modules within
|
||||||
// rootDir: undefined,
|
rootDir: process.cwd(),
|
||||||
|
|
||||||
// A list of paths to directories that Jest should use to search for files in
|
// A list of paths to directories that Jest should use to search for files in
|
||||||
// roots: [
|
// roots: [
|
||||||
// "<rootDir>"
|
// '<rootDir>/scripts'
|
||||||
// ],
|
// ],
|
||||||
|
|
||||||
// Allows you to use a custom runner instead of Jest's default test runner
|
// Allows you to use a custom runner instead of Jest's default test runner
|
||||||
// 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: [require.resolve('./scripts/jest/setupHostConfig.js')],
|
setupFilesAfterEnv: [require.resolve('./scripts/__tests__/jest/setupEnvironment.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: [],
|
setupFiles: [require.resolve('./scripts/__tests__/jest/setupTests.js')],
|
||||||
|
|
||||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||||
// snapshotSerializers: [],
|
// snapshotSerializers: [],
|
||||||
|
@ -150,9 +146,7 @@ module.exports = {
|
||||||
|
|
||||||
// The glob patterns Jest uses to detect test files
|
// The glob patterns Jest uses to detect test files
|
||||||
testMatch: [
|
testMatch: [
|
||||||
'<rootDir>/scripts/__tests__/**/*.test.js',
|
'<rootDir>/scripts/__tests__/**/*.test.js'
|
||||||
'<rootDir>/scripts/__tests__/*.test.js',
|
|
||||||
'<rootDir>/scripts/__tests__/*.test.jsx',
|
|
||||||
],
|
],
|
||||||
|
|
||||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||||
|
@ -173,7 +167,7 @@ module.exports = {
|
||||||
// testURL: "http://localhost",
|
// testURL: "http://localhost",
|
||||||
|
|
||||||
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
|
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
|
||||||
// timers: "real",
|
timers: 'fake',
|
||||||
|
|
||||||
// A map from regular expressions to paths to transformers
|
// A map from regular expressions to paths to transformers
|
||||||
// transform: undefined,
|
// transform: undefined,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { act } from 'react-dom/test-utils';
|
||||||
|
|
||||||
|
|
||||||
describe('Hook Test', () => {
|
describe('Hook Test', () => {
|
||||||
const { useState, useReducer, useEffect, useLayoutEffect, useContext } = React;
|
const { useState, useReducer, useEffect, useLayoutEffect, useContext, useMemo, useCallback, useRef, useImperativeHandle, forwardRef } = React;
|
||||||
const { unmountComponentAtNode } = HorizonDOM;
|
const { unmountComponentAtNode } = HorizonDOM;
|
||||||
let container = null;
|
let container = null;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -175,4 +175,127 @@ describe('Hook Test', () => {
|
||||||
expect(container.querySelector('p').innerHTML).toBe('audi');
|
expect(container.querySelector('p').innerHTML).toBe('audi');
|
||||||
expect(container.querySelector('#senP').innerHTML).toBe('88');
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
import * as React from '../../../libs/horizon/src/external/Horizon';
|
||||||
|
import * as HorizonDOM from '../../../libs/horizon/src/dom/DOMExternal';
|
||||||
|
//import { act } from 'react-dom/test-utils';
|
||||||
|
import * as Scheduler from 'scheduler';
|
||||||
|
|
||||||
|
describe('useState Hook Test', () => {
|
||||||
|
const { useState, forwardRef, useImperativeHandle, memo } = React;
|
||||||
|
const { unmountComponentAtNode } = HorizonDOM;
|
||||||
|
let container = null;
|
||||||
|
beforeEach(() => {
|
||||||
|
// 创建一个 DOM 元素作为渲染目标
|
||||||
|
container = document.createElement('div');
|
||||||
|
document.body.appendChild(container);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// 退出时进行清理
|
||||||
|
unmountComponentAtNode(container);
|
||||||
|
container.remove();
|
||||||
|
container = null;
|
||||||
|
Scheduler.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
const Text = (props) => {
|
||||||
|
Scheduler.unstable_yieldValue(props.text);
|
||||||
|
return <p>{props.text}</p>;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('多个useState', () => {
|
||||||
|
const App = () => {
|
||||||
|
const [num, setNum] = useState(0);
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
return (
|
||||||
|
<p
|
||||||
|
onClick={() => {
|
||||||
|
setNum(num + 1);
|
||||||
|
setCount(count + 2);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{num}{count}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
HorizonDOM.render(<App />, container);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('00');
|
||||||
|
container.querySelector('p').click();
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('12');
|
||||||
|
container.querySelector('p').click();
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('24');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('同一个useState声明的状态会被覆盖处理', () => {
|
||||||
|
const App = () => {
|
||||||
|
const [num, setNum] = useState(0);
|
||||||
|
return (
|
||||||
|
<p
|
||||||
|
onClick={() => {
|
||||||
|
setNum(num + 1);
|
||||||
|
setNum(num + 2);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{num}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
HorizonDOM.render(<App />, container);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('0');
|
||||||
|
container.querySelector('p').click();
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('2');
|
||||||
|
container.querySelector('p').click();
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('4');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('useState设置相同的值时不会重新渲染', () => {
|
||||||
|
let setNum;
|
||||||
|
const App = () => {
|
||||||
|
const [num, _setNum] = useState(0);
|
||||||
|
setNum = _setNum;
|
||||||
|
return <Text text={num} />;
|
||||||
|
}
|
||||||
|
HorizonDOM.render(<App />, container);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('0');
|
||||||
|
expect(Scheduler).toHaveYielded([0]);
|
||||||
|
// useState修改state 时,设置相同的值,函数组件不会重新渲染
|
||||||
|
setNum(0);
|
||||||
|
expect(Scheduler).toHaveYielded([]);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('0');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('useState的惰性初始化', () => {
|
||||||
|
//let data = null;
|
||||||
|
const App = forwardRef((props, ref) => {
|
||||||
|
const [num, setNum] = useState(() => {
|
||||||
|
// if (data === null) {
|
||||||
|
// data = ['initNum'];
|
||||||
|
// } else {
|
||||||
|
// data.push('initNum');
|
||||||
|
// }
|
||||||
|
Scheduler.unstable_yieldValue(props.initNum);
|
||||||
|
return props.initNum
|
||||||
|
});
|
||||||
|
useImperativeHandle(ref, () => ({ setNum }))
|
||||||
|
return <p>{num}</p>;
|
||||||
|
|
||||||
|
})
|
||||||
|
const ref = React.createRef(null);
|
||||||
|
HorizonDOM.render(<App initNum={1} ref={ref} />, container);
|
||||||
|
expect(Scheduler).toHaveYielded([1]);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||||
|
// 设置num为3
|
||||||
|
ref.current.setNum(3);
|
||||||
|
// 初始化函数只在初始渲染时被调用,所以Scheduler里的dataArray清空后没有新增。
|
||||||
|
expect(Scheduler).toHaveYielded([]);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('useState与memo一起使用', () => {
|
||||||
|
let setNum;
|
||||||
|
const App = memo((props) => {
|
||||||
|
const [num, _setNum] = useState(0);
|
||||||
|
setNum = _setNum;
|
||||||
|
return <Text text={num} />;
|
||||||
|
})
|
||||||
|
HorizonDOM.render(<App />, container);
|
||||||
|
expect(Scheduler).toHaveYielded([0]);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('0');
|
||||||
|
// 不会重新渲染
|
||||||
|
HorizonDOM.render(<App />, container);
|
||||||
|
expect(Scheduler).toHaveYielded([]);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('0');
|
||||||
|
// 会重新渲染
|
||||||
|
setNum(1)
|
||||||
|
expect(Scheduler).toHaveYielded([1]);
|
||||||
|
expect(container.querySelector('p').innerHTML).toBe('1');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,22 @@
|
||||||
|
function captureAssertion(fn) {
|
||||||
|
try {
|
||||||
|
fn();
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
pass: false,
|
||||||
|
message: () => error.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {pass: true};
|
||||||
|
}
|
||||||
|
|
||||||
|
function toHaveYielded(Scheduler, expectedYields) {
|
||||||
|
return captureAssertion(() => {
|
||||||
|
const actualYields = Scheduler.unstable_clearYields();
|
||||||
|
expect(actualYields).toEqual(expectedYields);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
toHaveYielded,
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
global.isDev = process.env.NODE_ENV === 'development';
|
||||||
|
global.MessageChannel = function MessageChannel() {
|
||||||
|
this.port1 = {};
|
||||||
|
this.port2 = {
|
||||||
|
postMessage() { }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
expect.extend({
|
||||||
|
...require('./customMatcher'),
|
||||||
|
});
|
|
@ -0,0 +1 @@
|
||||||
|
jest.mock('scheduler', () => require('./testUtil.js'));
|
|
@ -0,0 +1,34 @@
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
(function () {
|
||||||
|
let dataArray = null;
|
||||||
|
let isFlushing = false;
|
||||||
|
|
||||||
|
const unstable_yieldValue = (value) => {
|
||||||
|
if (dataArray === null) {
|
||||||
|
dataArray = [value];
|
||||||
|
} else {
|
||||||
|
dataArray.push(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const unstable_clearYields = () => {
|
||||||
|
if (dataArray === null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const values = dataArray;
|
||||||
|
dataArray = null;
|
||||||
|
return values;
|
||||||
|
};
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
if (isFlushing) {
|
||||||
|
throw new Error('Cannot reset while already flushing work.');
|
||||||
|
}
|
||||||
|
dataArray = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.reset = reset;
|
||||||
|
exports.unstable_yieldValue = unstable_yieldValue;
|
||||||
|
exports.unstable_clearYields = unstable_clearYields;
|
||||||
|
})();
|
||||||
|
}
|
Loading…
Reference in New Issue