Match-id-7f3b43e0f44981f1ede4e5b082c137a3f488ba7a
This commit is contained in:
commit
62b155d42d
|
@ -0,0 +1,43 @@
|
|||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import { getLogUtils } from '../jest/testUtils';
|
||||
|
||||
describe('Component Error Test', () => {
|
||||
const LogUtils = getLogUtils();
|
||||
it('createElement不能为null或undefined', () => {
|
||||
const NullElement = null;
|
||||
const UndefinedElement = undefined;
|
||||
|
||||
jest.spyOn(console, 'error').mockImplementation();
|
||||
expect(() => {
|
||||
Horizon.render(<NullElement />, document.createElement('div'));
|
||||
}).toThrow('Component type is invalid, got: null');
|
||||
|
||||
expect(() => {
|
||||
Horizon.render(<UndefinedElement />, document.createElement('div'));
|
||||
}).toThrow('Component type is invalid, got: undefined');
|
||||
|
||||
const App = () => {
|
||||
return <AppChild />;
|
||||
};
|
||||
|
||||
let AppChild = () => {
|
||||
return (
|
||||
<NullElement />
|
||||
);
|
||||
};
|
||||
|
||||
expect(() => {
|
||||
Horizon.render(<App />, document.createElement('div'));
|
||||
}).toThrow('Component type is invalid, got: null');
|
||||
|
||||
AppChild = () => {
|
||||
return (
|
||||
<UndefinedElement />
|
||||
);
|
||||
};
|
||||
|
||||
expect(() => {
|
||||
Horizon.render(<App />, document.createElement('div'));
|
||||
}).toThrow('Component type is invalid, got: undefined');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,474 @@
|
|||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import { Text } from '../jest/commonComponents';
|
||||
import { getLogUtils } from '../jest/testUtils';
|
||||
|
||||
describe('Fragment', () => {
|
||||
const LogUtils = getLogUtils();
|
||||
const {
|
||||
useEffect,
|
||||
useRef,
|
||||
act,
|
||||
} = Horizon;
|
||||
it('可以渲染空元素', () => {
|
||||
const element = (
|
||||
<Horizon.Fragment />
|
||||
);
|
||||
|
||||
Horizon.render(element, container);
|
||||
|
||||
expect(container.textContent).toBe('');
|
||||
});
|
||||
it('可以渲染单个元素', () => {
|
||||
const element = (
|
||||
<Horizon.Fragment>
|
||||
<Text text="Fragment" />
|
||||
</Horizon.Fragment>
|
||||
);
|
||||
|
||||
Horizon.render(element, container);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual(['Fragment']);
|
||||
expect(container.textContent).toBe('Fragment');
|
||||
});
|
||||
|
||||
it('可以渲染混合元素', () => {
|
||||
const element = (
|
||||
<Horizon.Fragment>
|
||||
Java and <Text text="JavaScript" />
|
||||
</Horizon.Fragment>
|
||||
);
|
||||
|
||||
Horizon.render(element, container);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual(['JavaScript']);
|
||||
expect(container.textContent).toBe('Java and JavaScript');
|
||||
});
|
||||
|
||||
it('可以渲染集合元素', () => {
|
||||
const App = [<Text text="Java" />, <Text text="JavaScript" />];
|
||||
const element = (
|
||||
<>
|
||||
{App}
|
||||
</>
|
||||
);
|
||||
|
||||
Horizon.render(element, container);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual(['Java', 'JavaScript']);
|
||||
expect(container.textContent).toBe('JavaJavaScript');
|
||||
});
|
||||
|
||||
it('元素被放进不同层级Fragment里时,状态不会保留', () => {
|
||||
const ChildApp = (props) => {
|
||||
const flag = useRef(true);
|
||||
useEffect(() => {
|
||||
if (flag.current) {
|
||||
flag.current = false;
|
||||
} else {
|
||||
LogUtils.log('useEffect');
|
||||
}
|
||||
});
|
||||
|
||||
return <p>{props.logo}</p>;
|
||||
};
|
||||
|
||||
const App = (props) => {
|
||||
return props.change ? (
|
||||
<>
|
||||
<ChildApp logo={1} />
|
||||
</>
|
||||
|
||||
) : (
|
||||
<>
|
||||
<>
|
||||
<ChildApp logo={2} />
|
||||
</>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
act(() => {
|
||||
Horizon.render(<App change={false} />, container);
|
||||
});
|
||||
// 切换到不同层级Fragment时,副作用状态不会保留
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
expect(container.textContent).toBe('2');
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
expect(container.textContent).toBe('1');
|
||||
});
|
||||
|
||||
it('元素被放进单层Fragment里,且在Fragment的顶部时,状态会保留', () => {
|
||||
const ChildApp = (props) => {
|
||||
const flag = useRef(true);
|
||||
useEffect(() => {
|
||||
if (flag.current) {
|
||||
flag.current = false;
|
||||
} else {
|
||||
LogUtils.log('useEffect');
|
||||
}
|
||||
});
|
||||
|
||||
return <p>{props.logo}</p>;
|
||||
};
|
||||
|
||||
const App = (props) => {
|
||||
return props.change ? (
|
||||
<ChildApp logo={1} />
|
||||
) : (
|
||||
<>
|
||||
<ChildApp logo={2} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
act(() => {
|
||||
Horizon.render(<App change={false} />, container);
|
||||
});
|
||||
// 状态会保留
|
||||
expect(LogUtils.getNotClear()).toEqual(['useEffect']);
|
||||
expect(container.textContent).toBe('2');
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual(['useEffect', 'useEffect']);
|
||||
expect(container.textContent).toBe('1');
|
||||
});
|
||||
|
||||
it('元素被放进单层Fragment里,但不在Fragment的顶部时,状态不会保留', () => {
|
||||
const ChildApp = (props) => {
|
||||
const flag = useRef(true);
|
||||
useEffect(() => {
|
||||
if (flag.current) {
|
||||
flag.current = false;
|
||||
} else {
|
||||
LogUtils.log('useEffect');
|
||||
}
|
||||
});
|
||||
|
||||
return <p>{props.logo}</p>;
|
||||
};
|
||||
|
||||
const App = (props) => {
|
||||
return props.change ? (
|
||||
<ChildApp logo={1} />
|
||||
) : (
|
||||
<>
|
||||
<div>123</div>
|
||||
<ChildApp logo={2} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
act(() => {
|
||||
Horizon.render(<App change={false} />, container);
|
||||
});
|
||||
// 状态不会保留
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
expect(container.textContent).toBe('1232');
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
expect(container.textContent).toBe('1');
|
||||
});
|
||||
|
||||
it('元素被放进多层Fragment里时,状态不会保留', () => {
|
||||
const ChildApp = (props) => {
|
||||
const flag = useRef(true);
|
||||
useEffect(() => {
|
||||
if (flag.current) {
|
||||
flag.current = false;
|
||||
} else {
|
||||
LogUtils.log('useEffect');
|
||||
}
|
||||
});
|
||||
|
||||
return <p>{props.logo}</p>;
|
||||
};
|
||||
|
||||
const App = (props) => {
|
||||
return props.change ? (
|
||||
<ChildApp logo={1} />
|
||||
) : (
|
||||
<>
|
||||
<>
|
||||
<>
|
||||
<ChildApp logo={2} />
|
||||
</>
|
||||
</>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
act(() => {
|
||||
Horizon.render(<App change={false} />, container);
|
||||
});
|
||||
// 状态不会保留
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
expect(container.textContent).toBe('2');
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
expect(container.textContent).toBe('1');
|
||||
});
|
||||
|
||||
it('元素被切换放进同级Fragment里时,状态会保留', () => {
|
||||
const ChildApp = (props) => {
|
||||
const flag = useRef(true);
|
||||
useEffect(() => {
|
||||
if (flag.current) {
|
||||
flag.current = false;
|
||||
} else {
|
||||
LogUtils.log('useEffect');
|
||||
}
|
||||
});
|
||||
|
||||
return <p>{props.logo}</p>;
|
||||
};
|
||||
|
||||
const App = (props) => {
|
||||
return props.change ? (
|
||||
<>
|
||||
<>
|
||||
<>
|
||||
<ChildApp logo={1} />
|
||||
</>
|
||||
</>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<>
|
||||
<>
|
||||
<ChildApp logo={2} />
|
||||
</>
|
||||
</>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
act(() => {
|
||||
Horizon.render(<App change={false} />, container);
|
||||
});
|
||||
// 状态会保留
|
||||
expect(LogUtils.getNotClear()).toEqual(['useEffect']);
|
||||
expect(container.textContent).toBe('2');
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual(['useEffect', 'useEffect']);
|
||||
expect(container.textContent).toBe('1');
|
||||
});
|
||||
|
||||
it('元素被切换放进同级Fragment,且在数组顶层时,状态会保留', () => {
|
||||
const ChildApp = (props) => {
|
||||
const flag = useRef(true);
|
||||
useEffect(() => {
|
||||
if (flag.current) {
|
||||
flag.current = false;
|
||||
} else {
|
||||
LogUtils.log('useEffect');
|
||||
}
|
||||
});
|
||||
|
||||
return <p>{props.logo}</p>;
|
||||
};
|
||||
|
||||
const App = (props) => {
|
||||
return props.change ? (
|
||||
<>
|
||||
<>
|
||||
<>
|
||||
<ChildApp logo={1} />
|
||||
</>
|
||||
</>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<>
|
||||
<>
|
||||
{[<ChildApp logo={2} />]}
|
||||
</>
|
||||
</>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
act(() => {
|
||||
Horizon.render(<App change={false} />, container);
|
||||
});
|
||||
// 状态会保留
|
||||
expect(LogUtils.getNotClear()).toEqual(['useEffect']);
|
||||
expect(container.textContent).toBe('2');
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual(['useEffect', 'useEffect']);
|
||||
expect(container.textContent).toBe('1');
|
||||
});
|
||||
|
||||
it('数组里的顶层元素被切换放进单级Fragment时,状态会保留', () => {
|
||||
const ChildApp = (props) => {
|
||||
const flag = useRef(true);
|
||||
useEffect(() => {
|
||||
if (flag.current) {
|
||||
flag.current = false;
|
||||
} else {
|
||||
LogUtils.log('useEffect');
|
||||
}
|
||||
});
|
||||
|
||||
return <p>{props.logo}</p>;
|
||||
};
|
||||
|
||||
const App = (props) => {
|
||||
return props.change ? (
|
||||
[<ChildApp logo={1} />]
|
||||
) : (
|
||||
<>
|
||||
<ChildApp logo={2} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
act(() => {
|
||||
Horizon.render(<App change={false} />, container);
|
||||
});
|
||||
// 状态会保留
|
||||
expect(LogUtils.getNotClear()).toEqual(['useEffect']);
|
||||
expect(container.textContent).toBe('2');
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual(['useEffect', 'useEffect']);
|
||||
expect(container.textContent).toBe('1');
|
||||
});
|
||||
|
||||
it('Fragment里的顶层数组里的顶层元素被切换放进不同级Fragment时,状态不会保留', () => {
|
||||
const ChildApp = (props) => {
|
||||
const flag = useRef(true);
|
||||
useEffect(() => {
|
||||
if (flag.current) {
|
||||
flag.current = false;
|
||||
} else {
|
||||
LogUtils.log('useEffect');
|
||||
}
|
||||
});
|
||||
|
||||
return <p>{props.logo}</p>;
|
||||
};
|
||||
|
||||
const App = (props) => {
|
||||
return props.change ? (
|
||||
<>
|
||||
[<ChildApp logo={1} />]
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<>
|
||||
<ChildApp logo={2} />
|
||||
</>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
act(() => {
|
||||
Horizon.render(<App change={false} />, container);
|
||||
});
|
||||
// 状态会保留
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
expect(container.textContent).toBe('2');
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
expect(container.textContent).toBe('[1]');
|
||||
});
|
||||
|
||||
it('Fragment的key值不同时,状态不会保留', () => {
|
||||
const ChildApp = (props) => {
|
||||
const flag = useRef(true);
|
||||
useEffect(() => {
|
||||
if (flag.current) {
|
||||
flag.current = false;
|
||||
} else {
|
||||
LogUtils.log('useEffect');
|
||||
}
|
||||
});
|
||||
|
||||
return <p>{props.logo}</p>;
|
||||
};
|
||||
|
||||
const App = (props) => {
|
||||
return props.change ? (
|
||||
<Horizon.Fragment key='hf'>
|
||||
<ChildApp logo={1} />
|
||||
</Horizon.Fragment>
|
||||
) : (
|
||||
<Horizon.Fragment key='nhf'>
|
||||
<ChildApp logo={2} />
|
||||
</Horizon.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
act(() => {
|
||||
Horizon.render(<App change={false} />, container);
|
||||
});
|
||||
// 状态不会保留
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
expect(container.textContent).toBe('2');
|
||||
|
||||
act(() => {
|
||||
Horizon.render(<App change={true} />, container);
|
||||
});
|
||||
expect(LogUtils.getNotClear()).toEqual([]);
|
||||
expect(container.textContent).toBe('1');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,187 @@
|
|||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import { Text } from '../jest/commonComponents';
|
||||
import { getLogUtils } from '../jest/testUtils';
|
||||
|
||||
describe('LazyComponent Test', () => {
|
||||
const LogUtils = getLogUtils();
|
||||
const mockImport = jest.fn(async (component) => {
|
||||
return { default: component };
|
||||
});
|
||||
|
||||
it('Horizon.lazy()', async () => {
|
||||
class LazyComponent extends Horizon.Component {
|
||||
static defaultProps = { language: 'Java' };
|
||||
|
||||
render() {
|
||||
const text = `${this.props.greeting}: ${this.props.language}`;
|
||||
return <span>{text}</span>;
|
||||
}
|
||||
}
|
||||
|
||||
const Lazy = Horizon.lazy(() => mockImport(LazyComponent));
|
||||
|
||||
Horizon.render(
|
||||
<Horizon.Suspense fallback={<Text text="Loading..." />}>
|
||||
<Lazy greeting="Hi" />
|
||||
</Horizon.Suspense>,
|
||||
container
|
||||
);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual(['Loading...']);
|
||||
expect(container.textContent).toBe('Loading...');
|
||||
expect(container.querySelector('span')).toBe(null);
|
||||
|
||||
await Promise.resolve();
|
||||
Horizon.render(
|
||||
<Horizon.Suspense fallback={<Text text="Loading..." />}>
|
||||
<Lazy greeting="Goodbye" />
|
||||
</Horizon.Suspense>,
|
||||
container
|
||||
);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual([]);
|
||||
expect(container.querySelector('span').innerHTML).toBe('Goodbye: Java');
|
||||
});
|
||||
|
||||
it('同步解析', async () => {
|
||||
const LazyApp = Horizon.lazy(() => ({
|
||||
then(cb) {
|
||||
cb({ default: Text });
|
||||
},
|
||||
}));
|
||||
|
||||
Horizon.render(
|
||||
<Horizon.Suspense fallback={<div>Loading...</div>}>
|
||||
<LazyApp text="Lazy" />
|
||||
</Horizon.Suspense>,
|
||||
container
|
||||
);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual(['Lazy']);
|
||||
expect(container.textContent).toBe('Lazy');
|
||||
});
|
||||
|
||||
it('异常捕获边界', async () => {
|
||||
class ErrorBoundary extends Horizon.Component {
|
||||
state = {};
|
||||
static getDerivedStateFromError(error) {
|
||||
return { message: error.message };
|
||||
}
|
||||
render() {
|
||||
return this.state.message
|
||||
? <h2>Error: {this.state.message}</h2>
|
||||
: this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
const LazyComponent = () => {
|
||||
const [num, setNum] = Horizon.useState(0);
|
||||
if (num === 2) {
|
||||
throw new Error('num is 2');
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<p id="p">{num}</p>
|
||||
<button onClick={() => setNum(num + 1)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const LazyApp = Horizon.lazy(() => mockImport(LazyComponent));
|
||||
|
||||
Horizon.render(
|
||||
<ErrorBoundary>
|
||||
<Horizon.Suspense fallback={<div>Loading...</div>}>
|
||||
<LazyApp />
|
||||
</Horizon.Suspense>
|
||||
</ErrorBoundary>,
|
||||
container
|
||||
);
|
||||
expect(container.textContent).toBe('Loading...');
|
||||
|
||||
await Promise.resolve();
|
||||
Horizon.render(
|
||||
<ErrorBoundary>
|
||||
<Horizon.Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyApp />
|
||||
</Horizon.Suspense>
|
||||
</ErrorBoundary>,
|
||||
container
|
||||
);
|
||||
expect(container.textContent).toBe('0');
|
||||
container.querySelector('button').click();
|
||||
expect(container.textContent).toBe('1');
|
||||
jest.spyOn(console, 'error').mockImplementation();
|
||||
container.querySelector('button').click();
|
||||
expect(container.textContent).toBe('Error: num is 2');
|
||||
});
|
||||
|
||||
it('componentDidCatch捕获异常', async () => {
|
||||
class ErrorBoundary extends Horizon.Component {
|
||||
state = {
|
||||
catchError: false,
|
||||
error: null,
|
||||
componentStack: null
|
||||
};
|
||||
componentDidCatch(error, info) {
|
||||
if(error){
|
||||
this.setState({
|
||||
catchError: true,
|
||||
error,
|
||||
componentStack: info.componentStack
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
render() {
|
||||
return this.state.catchError
|
||||
? <h2>Error: {this.state.error.message}</h2>
|
||||
: this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
const LazyComponent = () => {
|
||||
const [num, setNum] = Horizon.useState(0);
|
||||
if (num === 2) {
|
||||
throw new Error('num is 2');
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<p id="p">{num}</p>
|
||||
<button onClick={() => setNum(num + 1)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const LazyApp = Horizon.lazy(() => mockImport(LazyComponent));
|
||||
|
||||
Horizon.render(
|
||||
<ErrorBoundary>
|
||||
<Horizon.Suspense fallback={<div>Loading...</div>}>
|
||||
<LazyApp />
|
||||
</Horizon.Suspense>
|
||||
</ErrorBoundary>,
|
||||
container
|
||||
);
|
||||
expect(container.textContent).toBe('Loading...');
|
||||
|
||||
await Promise.resolve();
|
||||
Horizon.render(
|
||||
<ErrorBoundary>
|
||||
<Horizon.Suspense fallback={<Text text="Loading..." />}>
|
||||
<LazyApp />
|
||||
</Horizon.Suspense>
|
||||
</ErrorBoundary>,
|
||||
container
|
||||
);
|
||||
|
||||
expect(container.textContent).toBe('0');
|
||||
container.querySelector('button').click();
|
||||
expect(container.textContent).toBe('1');
|
||||
jest.spyOn(console, 'error').mockImplementation();
|
||||
container.querySelector('button').click();
|
||||
expect(container.textContent).toBe('Error: num is 2');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,205 @@
|
|||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import { getLogUtils } from '../jest/testUtils';
|
||||
|
||||
describe('PortalComponent Test', () => {
|
||||
const LogUtils = getLogUtils();
|
||||
|
||||
it('将子节点渲染到存在于父组件以外的 DOM 节点', () => {
|
||||
const portalRoot = document.createElement('div');
|
||||
|
||||
class PortalApp extends Horizon.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.element = portalRoot;
|
||||
}
|
||||
|
||||
render() {
|
||||
return Horizon.createPortal(
|
||||
this.props.child,
|
||||
this.element,
|
||||
);
|
||||
}
|
||||
}
|
||||
Horizon.render(<PortalApp child={<div>PortalApp</div>} />, container);
|
||||
expect(container.textContent).toBe('');
|
||||
// <div>PortalApp</div>被渲染到了portalRoot而非container
|
||||
expect(portalRoot.textContent).toBe('PortalApp');
|
||||
|
||||
Horizon.unmountComponentAtNode(container);
|
||||
expect(container.textContent).toBe('');
|
||||
expect(portalRoot.textContent).toBe('');
|
||||
});
|
||||
|
||||
it('渲染多个Portal', () => {
|
||||
const portalRoot1st = document.createElement('div');
|
||||
const portalRoot2nd = document.createElement('div');
|
||||
|
||||
class PortalApp extends Horizon.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.element = portalRoot1st;
|
||||
this.newElement = portalRoot2nd;
|
||||
}
|
||||
|
||||
render() {
|
||||
return [
|
||||
Horizon.createPortal(
|
||||
this.props.child,
|
||||
this.element,
|
||||
),
|
||||
Horizon.createPortal(
|
||||
this.props.child,
|
||||
this.newElement,
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
Horizon.render(<PortalApp child={<div>PortalApp</div>} />, container);
|
||||
expect(container.textContent).toBe('');
|
||||
// <div>PortalApp</div>被渲染到了portalRoot而非container
|
||||
expect(portalRoot1st.textContent).toBe('PortalApp');
|
||||
expect(portalRoot2nd.textContent).toBe('PortalApp');
|
||||
|
||||
Horizon.unmountComponentAtNode(container);
|
||||
expect(container.textContent).toBe('');
|
||||
expect(portalRoot1st.textContent).toBe('');
|
||||
expect(portalRoot2nd.textContent).toBe('');
|
||||
});
|
||||
|
||||
it('渲染最近的Portal', () => {
|
||||
const portalRoot1st = document.createElement('div');
|
||||
const portalRoot2nd = document.createElement('div');
|
||||
const portalRoot3rd = document.createElement('div');
|
||||
|
||||
class PortalApp extends Horizon.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.element = portalRoot1st;
|
||||
this.newElement = portalRoot2nd;
|
||||
this.element3rd = portalRoot3rd;
|
||||
}
|
||||
|
||||
render() {
|
||||
return [
|
||||
<div>PortalApp1st</div>,
|
||||
Horizon.createPortal([
|
||||
<div>PortalApp4</div>,
|
||||
Horizon.createPortal(
|
||||
this.props.child,
|
||||
this.element3rd,
|
||||
),
|
||||
], this.element),
|
||||
<div>PortalApp2nd</div>,
|
||||
Horizon.createPortal(
|
||||
this.props.child,
|
||||
this.newElement,
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
Horizon.render(<PortalApp child={<div>PortalApp</div>} />, container);
|
||||
expect(container.textContent).toBe('PortalApp1stPortalApp2nd');
|
||||
// <div>PortalApp4</div>会挂载在this.element上
|
||||
expect(portalRoot1st.textContent).toBe('PortalApp4');
|
||||
expect(portalRoot2nd.textContent).toBe('PortalApp');
|
||||
expect(portalRoot3rd.textContent).toBe('PortalApp');
|
||||
|
||||
Horizon.unmountComponentAtNode(container);
|
||||
expect(container.textContent).toBe('');
|
||||
expect(portalRoot1st.textContent).toBe('');
|
||||
expect(portalRoot2nd.textContent).toBe('');
|
||||
});
|
||||
|
||||
it('改变Portal的参数', () => {
|
||||
const portalRoot = document.createElement('div');
|
||||
|
||||
class PortalApp extends Horizon.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.element = portalRoot;
|
||||
}
|
||||
|
||||
render() {
|
||||
return Horizon.createPortal(
|
||||
this.props.child,
|
||||
this.element,
|
||||
);
|
||||
}
|
||||
}
|
||||
Horizon.render(<PortalApp key='portal' child={<div>PortalApp</div>} />, container);
|
||||
expect(container.textContent).toBe('');
|
||||
expect(portalRoot.textContent).toBe('PortalApp');
|
||||
|
||||
Horizon.render(<PortalApp key='portal' child={<div>AppPortal</div>} />, container);
|
||||
expect(container.textContent).toBe('');
|
||||
expect(portalRoot.textContent).toBe('AppPortal');
|
||||
|
||||
Horizon.render(<PortalApp key='portal' child={['por', 'tal']} />, container);
|
||||
expect(container.textContent).toBe('');
|
||||
expect(portalRoot.textContent).toBe('portal');
|
||||
|
||||
Horizon.render(<PortalApp key='portal' child={null} />, container);
|
||||
expect(container.textContent).toBe('');
|
||||
expect(portalRoot.textContent).toBe('');
|
||||
|
||||
Horizon.unmountComponentAtNode(container);
|
||||
expect(container.textContent).toBe('');
|
||||
expect(portalRoot.textContent).toBe('');
|
||||
});
|
||||
|
||||
it('通过Portal进行事件冒泡', () => {
|
||||
const portalRoot = document.createElement('div');
|
||||
const buttonRef = Horizon.createRef();
|
||||
|
||||
class PortalApp extends Horizon.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.element = portalRoot;
|
||||
}
|
||||
|
||||
render() {
|
||||
return Horizon.createPortal(
|
||||
this.props.child,
|
||||
this.element,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const Child = () => {
|
||||
return (
|
||||
<div>
|
||||
<button ref={buttonRef}>Click</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const App = () => {
|
||||
const handleClick = () => {
|
||||
LogUtils.log('bubble click event');
|
||||
};
|
||||
|
||||
const handleCaptureClick = () => {
|
||||
LogUtils.log('capture click event');
|
||||
};
|
||||
|
||||
return (
|
||||
<div onClickCapture={handleCaptureClick()} onClick={handleClick()}>
|
||||
<PortalApp child={<Child />}>
|
||||
|
||||
</PortalApp>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Horizon.render(<App />, container);
|
||||
const event = document.createEvent('Event');
|
||||
event.initEvent('click', true, true);
|
||||
buttonRef.current.dispatchEvent(event);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual([
|
||||
// 从外到内先捕获再冒泡
|
||||
'capture click event',
|
||||
'bubble click event'
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import { Text } from '../jest/commonComponents';
|
||||
import { getLogUtils } from '../jest/testUtils';
|
||||
|
||||
describe('SuspenseComponent Test', () => {
|
||||
const LogUtils = getLogUtils();
|
||||
const mockImport = jest.fn(async (component) => {
|
||||
return { default: component };
|
||||
});
|
||||
|
||||
// var EMPTY_OBJECT = {};
|
||||
// const mockCreateResource = jest.fn((component) => {
|
||||
// let result = EMPTY_OBJECT;
|
||||
// return () =>{
|
||||
// component().then(res => {
|
||||
// LogUtils.log(res);
|
||||
// result = res;
|
||||
// }, reason => {
|
||||
// LogUtils.log(reason);
|
||||
// });
|
||||
// if(result === EMPTY_OBJECT){
|
||||
// throw component();
|
||||
// }
|
||||
// return result;
|
||||
// };
|
||||
// });
|
||||
|
||||
it('挂载lazy组件', async () => {
|
||||
// 用同步的代码来实现异步操作
|
||||
class LazyComponent extends Horizon.Component {
|
||||
render() {
|
||||
return <Text text={this.props.num} />;
|
||||
}
|
||||
}
|
||||
|
||||
const Lazy = Horizon.lazy(() => mockImport(LazyComponent));
|
||||
|
||||
Horizon.render(
|
||||
<Horizon.Suspense fallback={<Text text="Loading..." />}>
|
||||
<Lazy num={5} />
|
||||
</Horizon.Suspense>,
|
||||
container
|
||||
);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual(['Loading...']);
|
||||
expect(container.textContent).toBe('Loading...');
|
||||
|
||||
await Promise.resolve();
|
||||
Horizon.render(
|
||||
<Horizon.Suspense fallback={<Text text="Loading..." />}>
|
||||
<Lazy num={5} />
|
||||
</Horizon.Suspense>,
|
||||
container
|
||||
);
|
||||
expect(LogUtils.getAndClear()).toEqual([5]);
|
||||
expect(container.querySelector('p').innerHTML).toBe('5');
|
||||
});
|
||||
|
||||
});
|
|
@ -7,26 +7,60 @@ export const stopBubbleOrCapture = (e, value) => {
|
|||
e.stopPropagation();
|
||||
};
|
||||
|
||||
export const getEventListeners = (dom) => {
|
||||
let ret = true;
|
||||
let keyArray = [];
|
||||
for (let key in dom) {
|
||||
keyArray.push(key);
|
||||
function listAllEventListeners() {
|
||||
const allElements = Array.prototype.slice.call(document.querySelectorAll('*'));
|
||||
allElements.push(document);
|
||||
allElements.push(window);
|
||||
|
||||
const types = [];
|
||||
|
||||
for (let ev in window) {
|
||||
if (/^on/.test(ev)) types[types.length] = ev;
|
||||
}
|
||||
console.log(keyArray);
|
||||
console.log('---------------------------------');
|
||||
console.log(allDelegatedNativeEvents);
|
||||
try {
|
||||
allDelegatedNativeEvents.forEach(event => {
|
||||
if (!keyArray.includes(event)) {
|
||||
ret = false;
|
||||
throw new Error('没有挂载全量事件');
|
||||
|
||||
let elements = [];
|
||||
for (let i = 0; i < allElements.length; i++) {
|
||||
const currentElement = allElements[i];
|
||||
for (let j = 0; j < types.length; j++) {
|
||||
if (typeof currentElement[types[j]] === 'function') {
|
||||
elements.push({
|
||||
'node': currentElement,
|
||||
'type': types[j],
|
||||
'func': currentElement[types[j]].toString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
return elements.sort(function(a,b) {
|
||||
return a.type.localeCompare(b.type);
|
||||
});
|
||||
}
|
||||
|
||||
export const getEventListeners = (dom) => {
|
||||
console.table(listAllEventListeners());
|
||||
|
||||
|
||||
|
||||
// let ret = true;
|
||||
// let keyArray = [];
|
||||
// for (let key in dom) {
|
||||
// if (/^on/.test(key)) keyArray.push(key);
|
||||
// }
|
||||
// console.log(getEventListeners);
|
||||
// console.log('---------------------------------');
|
||||
// console.log(allDelegatedNativeEvents);
|
||||
// try {
|
||||
// allDelegatedNativeEvents.forEach(event => {
|
||||
// if (!keyArray.includes(event)) {
|
||||
// ret = false;
|
||||
// throw new Error('没有挂载全量事件');
|
||||
// }
|
||||
// });
|
||||
// } catch (error) {
|
||||
// console.log(error);
|
||||
// }
|
||||
// return ret;
|
||||
};
|
||||
|
||||
export function triggerClickEvent(container, id) {
|
||||
|
@ -58,6 +92,10 @@ class LogUtils {
|
|||
return values;
|
||||
};
|
||||
|
||||
getNotClear = () => {
|
||||
return this.dataArray === null ? [] : this.dataArray;
|
||||
};
|
||||
|
||||
clear = () => {
|
||||
this.dataArray = this.dataArray ? null : this.dataArray;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue