mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-03-06 17:34:59 +08:00
test: add test to check if action is handled correctly
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { ScreenProps } from './types';
|
||||
|
||||
export default function Screen(_: ScreenProps) {
|
||||
/* istanbul ignore next */
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import { render } from 'react-native-testing-library';
|
||||
import Screen from '../Screen';
|
||||
import NavigationContainer from '../NavigationContainer';
|
||||
import useNavigationBuilder from '../useNavigationBuilder';
|
||||
import { Router } from '../types';
|
||||
import Screen from '../Screen';
|
||||
|
||||
const MockRouter: Router<{ type: 'UPDATE' | 'NOOP' }> = {
|
||||
const MockRouter: Router<{ type: string }> = {
|
||||
getInitialState({ screens, initialRouteName }) {
|
||||
const routeNames = Object.keys(screens);
|
||||
|
||||
@@ -26,10 +26,10 @@ const MockRouter: Router<{ type: 'UPDATE' | 'NOOP' }> = {
|
||||
return { ...state };
|
||||
|
||||
case 'NOOP':
|
||||
return null;
|
||||
return state;
|
||||
|
||||
default:
|
||||
return state;
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -37,7 +37,7 @@ const MockRouter: Router<{ type: 'UPDATE' | 'NOOP' }> = {
|
||||
};
|
||||
|
||||
it('initializes state for a navigator on navigation', () => {
|
||||
expect.assertions(1);
|
||||
expect.assertions(2);
|
||||
|
||||
const TestNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
@@ -56,34 +56,254 @@ it('initializes state for a navigator on navigation', () => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer
|
||||
onStateChange={state =>
|
||||
expect(state).toEqual({
|
||||
index: 0,
|
||||
key: 'root',
|
||||
names: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
{ key: 'baz', name: 'baz' },
|
||||
],
|
||||
})
|
||||
}
|
||||
>
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="foo">
|
||||
<Screen name="foo" component={FooScreen} />
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
<Screen name="baz" component={jest.fn()} />
|
||||
<Screen name="baz">
|
||||
{() => (
|
||||
<TestNavigator>
|
||||
<Screen name="qux" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).toBeCalledWith({
|
||||
index: 0,
|
||||
key: 'root',
|
||||
names: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
{ key: 'baz', name: 'baz' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('initializes state for nested navigator on navigation', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const TestNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
};
|
||||
|
||||
const TestScreen = (props: any) => {
|
||||
React.useEffect(() => {
|
||||
props.navigation.dispatch({ type: 'UPDATE' });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="baz">
|
||||
<Screen name="foo" component={jest.fn()} />
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
<Screen name="baz">
|
||||
{() => (
|
||||
<TestNavigator>
|
||||
<Screen name="qux" component={TestScreen} />
|
||||
</TestNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).toBeCalledWith({
|
||||
index: 2,
|
||||
key: 'root',
|
||||
names: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'foo', name: 'foo' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
{
|
||||
key: 'baz',
|
||||
name: 'baz',
|
||||
state: {
|
||||
index: 0,
|
||||
key: 'root',
|
||||
names: ['qux'],
|
||||
routes: [{ key: 'qux', name: 'qux' }],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("doesn't update state if nothing changed", () => {
|
||||
expect.assertions(1);
|
||||
|
||||
const TestNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
};
|
||||
|
||||
const FooScreen = (props: any) => {
|
||||
React.useEffect(() => {
|
||||
props.navigation.dispatch({ type: 'NOOP' });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="foo">
|
||||
<Screen name="foo" component={FooScreen} />
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
it("doesn't update state if action wasn't handled", () => {
|
||||
expect.assertions(1);
|
||||
|
||||
const TestNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
};
|
||||
|
||||
const FooScreen = (props: any) => {
|
||||
React.useEffect(() => {
|
||||
props.navigation.dispatch({ type: 'INVALID' });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="foo">
|
||||
<Screen name="foo" component={FooScreen} />
|
||||
<Screen name="bar" component={jest.fn()} />
|
||||
</TestNavigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
it("lets parent handle the action if child didn't", () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const ParentRouter: Router<{ type: string }> = {
|
||||
...MockRouter,
|
||||
|
||||
getStateForAction(state, action) {
|
||||
if (action.type === 'REVERSE') {
|
||||
return {
|
||||
...state,
|
||||
routes: state.routes.slice().reverse(),
|
||||
};
|
||||
}
|
||||
|
||||
return MockRouter.getStateForAction(state, action);
|
||||
},
|
||||
};
|
||||
|
||||
const ParentNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(
|
||||
ParentRouter,
|
||||
props
|
||||
);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
};
|
||||
|
||||
const ChildNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
|
||||
return descriptors[
|
||||
navigation.state.routes[navigation.state.index].key
|
||||
].render();
|
||||
};
|
||||
|
||||
const TestScreen = (props: any) => {
|
||||
React.useEffect(() => {
|
||||
props.navigation.dispatch({ type: 'REVERSE' });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<ParentNavigator initialRouteName="baz">
|
||||
<Screen name="foo">{() => null}</Screen>
|
||||
<Screen name="bar">{() => null}</Screen>
|
||||
<Screen name="baz">
|
||||
{() => (
|
||||
<ChildNavigator>
|
||||
<Screen name="qux" component={TestScreen} />
|
||||
</ChildNavigator>
|
||||
)}
|
||||
</Screen>
|
||||
</ParentNavigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).toBeCalledWith({
|
||||
index: 2,
|
||||
key: 'root',
|
||||
names: ['foo', 'bar', 'baz'],
|
||||
routes: [
|
||||
{ key: 'baz', name: 'baz' },
|
||||
{ key: 'bar', name: 'bar' },
|
||||
{ key: 'foo', name: 'foo' },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('allows arbitrary state updates by dispatching a function', () => {
|
||||
expect.assertions(1);
|
||||
expect.assertions(2);
|
||||
|
||||
const TestNavigator = (props: any) => {
|
||||
const { navigation, descriptors } = useNavigationBuilder(MockRouter, props);
|
||||
@@ -108,17 +328,10 @@ it('allows arbitrary state updates by dispatching a function', () => {
|
||||
|
||||
const BarScreen = () => null;
|
||||
|
||||
const onStateChange = jest.fn();
|
||||
|
||||
const element = (
|
||||
<NavigationContainer
|
||||
onStateChange={state =>
|
||||
expect(state).toEqual({
|
||||
index: 1,
|
||||
key: 'root',
|
||||
names: ['foo', 'bar'],
|
||||
routes: [{ key: 'bar', name: 'bar' }, { key: 'foo', name: 'foo' }],
|
||||
})
|
||||
}
|
||||
>
|
||||
<NavigationContainer onStateChange={onStateChange}>
|
||||
<TestNavigator initialRouteName="foo">
|
||||
<Screen name="foo" component={FooScreen} />
|
||||
<Screen name="bar" component={BarScreen} />
|
||||
@@ -127,6 +340,14 @@ it('allows arbitrary state updates by dispatching a function', () => {
|
||||
);
|
||||
|
||||
render(element).update(element);
|
||||
|
||||
expect(onStateChange).toBeCalledTimes(1);
|
||||
expect(onStateChange).toBeCalledWith({
|
||||
index: 1,
|
||||
key: 'root',
|
||||
names: ['foo', 'bar'],
|
||||
routes: [{ key: 'bar', name: 'bar' }, { key: 'foo', name: 'foo' }],
|
||||
});
|
||||
});
|
||||
|
||||
it('throws if navigator is not inside a container', () => {
|
||||
|
||||
@@ -23,7 +23,6 @@ export default function useOnAction({
|
||||
return React.useCallback(
|
||||
(action: NavigationAction) => {
|
||||
const state = getState();
|
||||
|
||||
const result = getStateForAction(state, action);
|
||||
|
||||
if (result !== null) {
|
||||
|
||||
Reference in New Issue
Block a user