Add basic working unit tests

This commit is contained in:
Brandon Presley 2022-10-30 12:56:58 +13:00
parent cc97c760bb
commit 6e75614d10
19 changed files with 10926 additions and 76 deletions

View File

@ -8,7 +8,7 @@ import {useColorScheme} from 'react-native';
import { import {
DarkTheme as PaperDarkTheme, DarkTheme as PaperDarkTheme,
DefaultTheme as PaperDefaultTheme, DefaultTheme as PaperDefaultTheme,
Provider, Provider as PaperProvider,
} from 'react-native-paper'; } from 'react-native-paper';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import {Color} from './color'; import {Color} from './color';
@ -75,7 +75,7 @@ const App = () => {
return ( return (
<Color.Provider value={{color, setColor}}> <Color.Provider value={{color, setColor}}>
<Provider <PaperProvider
theme={theme} theme={theme}
settings={{icon: props => <MaterialIcon {...props} />}}> settings={{icon: props => <MaterialIcon {...props} />}}>
<NavigationContainer theme={theme}> <NavigationContainer theme={theme}>
@ -87,7 +87,7 @@ const App = () => {
)} )}
</MassiveSnack> </MassiveSnack>
</NavigationContainer> </NavigationContainer>
</Provider> </PaperProvider>
</Color.Provider> </Color.Provider>
); );
}; };

View File

@ -12,7 +12,6 @@ import Page from './Page';
import Set from './set'; import Set from './set';
import {defaultSet, getSets, getToday} from './set.service'; import {defaultSet, getSets, getToday} from './set.service';
import SetItem from './SetItem'; import SetItem from './SetItem';
import {useSettings} from './use-settings';
const limit = 15; const limit = 15;
@ -22,27 +21,22 @@ export default function SetList() {
const [offset, setOffset] = useState(0); const [offset, setOffset] = useState(0);
const [term, setTerm] = useState(''); const [term, setTerm] = useState('');
const [end, setEnd] = useState(false); const [end, setEnd] = useState(false);
const {settings} = useSettings();
const navigation = useNavigation<NavigationProp<HomePageParams>>(); const navigation = useNavigation<NavigationProp<HomePageParams>>();
const refresh = useCallback( const refresh = useCallback(async (value: string) => {
async (value: string) => { const todaysSet = await getToday();
const todaysSet = await getToday(); if (todaysSet) setSet({...todaysSet});
if (todaysSet) setSet({...todaysSet}); const newSets = await getSets({
const newSets = await getSets({ term: `%${value}%`,
term: `%${value}%`, limit,
limit, offset: 0,
offset: 0, });
format: settings.date || '%Y-%m-%d %H:%M', console.log(`${SetList.name}.refresh:`, {first: newSets[0]});
}); if (newSets.length === 0) return setSets([]);
console.log(`${SetList.name}.refresh:`, {first: newSets[0]}); setSets(newSets);
if (newSets.length === 0) return setSets([]); setOffset(0);
setSets(newSets); setEnd(false);
setOffset(0); }, []);
setEnd(false);
},
[settings.date],
);
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {

View File

@ -47,7 +47,7 @@ export default function StartPlan() {
setCounts(newCounts); setCounts(newCounts);
console.log(`${StartPlan.name}.focus:`, {newCounts}); console.log(`${StartPlan.name}.focus:`, {newCounts});
}); });
}, [params]), }, [workouts]),
); );
const handleSubmit = async () => { const handleSubmit = async () => {
@ -101,7 +101,7 @@ export default function StartPlan() {
setUnit(newBest.unit); setUnit(newBest.unit);
setBest(newBest); setBest(newBest);
}, },
[name, workouts], [name, counts],
); );
return ( return (

View File

@ -10,10 +10,10 @@ import {WorkoutsPageParams} from './WorkoutsPage';
export default function WorkoutItem({ export default function WorkoutItem({
item, item,
onRemoved, onRemove,
}: { }: {
item: Set; item: Set;
onRemoved: () => void; onRemove: () => void;
}) { }) {
const [showMenu, setShowMenu] = useState(false); const [showMenu, setShowMenu] = useState(false);
const [anchor, setAnchor] = useState({x: 0, y: 0}); const [anchor, setAnchor] = useState({x: 0, y: 0});
@ -24,8 +24,8 @@ export default function WorkoutItem({
const remove = useCallback(async () => { const remove = useCallback(async () => {
await deleteSetsBy(item.name); await deleteSetsBy(item.name);
setShowMenu(false); setShowMenu(false);
onRemoved(); onRemove();
}, [setShowMenu, onRemoved, item.name]); }, [setShowMenu, onRemove, item.name]);
const longPress = useCallback( const longPress = useCallback(
(e: GestureResponderEvent) => { (e: GestureResponderEvent) => {

View File

@ -43,11 +43,7 @@ export default function WorkoutList() {
const renderItem = useCallback( const renderItem = useCallback(
({item}: {item: Set}) => ( ({item}: {item: Set}) => (
<WorkoutItem <WorkoutItem item={item} key={item.name} onRemove={() => refresh(term)} />
item={item}
key={item.name}
onRemoved={() => refresh(term)}
/>
), ),
[refresh, term], [refresh, term],
); );

View File

@ -1,14 +0,0 @@
/**
* @format
*/
import 'react-native';
import React from 'react';
import App from '../App';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
it('renders correctly', () => {
renderer.create(<App />);
});

View File

@ -0,0 +1,15 @@
import {render, screen} from '@testing-library/react-native';
import React from 'react';
import 'react-native';
import BestList from '../BestList';
import {MockProviders} from '../mock-providers';
it('renders correctly', () => {
render(
<MockProviders>
<BestList />
</MockProviders>,
);
expect(screen.getByText('Best')).toBeDefined();
expect(screen.getByPlaceholderText('Search')).toBeDefined();
});

View File

@ -0,0 +1,26 @@
import {render, screen} from '@testing-library/react-native';
import React from 'react';
import 'react-native';
import {MockProviders} from '../mock-providers';
import {Plan} from '../plan';
import PlanItem from '../PlanItem';
const plan: Plan = {
days: 'Monday,Tuesday,Wednesday',
workouts: 'Bench press,Bicep curls,Overhead press',
};
it('renders correctly', () => {
const onRemove = jest.fn();
render(
<MockProviders>
<PlanItem item={plan} onRemove={onRemove} />
</MockProviders>,
);
expect(screen.getByText(/Monday/i)).toBeDefined();
expect(screen.getByText(/Tuesday/i)).toBeDefined();
expect(screen.getByText(/Wednesday/i)).toBeDefined();
expect(screen.getByText(/Bench press/i)).toBeDefined();
expect(screen.getByText(/Bicep curls/i)).toBeDefined();
expect(screen.getByText(/Overhead press/i)).toBeDefined();
});

View File

@ -0,0 +1,15 @@
import {render, screen} from '@testing-library/react-native';
import React from 'react';
import 'react-native';
import {MockProviders} from '../mock-providers';
import PlanList from '../PlanList';
it('renders correctly', () => {
render(
<MockProviders>
<PlanList />
</MockProviders>,
);
expect(screen.getByText('Plans')).toBeDefined();
expect(screen.getByPlaceholderText('Search')).toBeDefined();
});

View File

@ -0,0 +1,26 @@
import {render, screen} from '@testing-library/react-native';
import React from 'react';
import 'react-native';
import {MockProviders} from '../mock-providers';
import Set from '../set';
import SetItem from '../SetItem';
const set: Set = {
name: 'Bench press',
reps: 6,
weight: 20,
};
it('renders correctly', () => {
const onRemove = jest.fn();
render(
<MockProviders>
<SetItem item={set} onRemove={onRemove} />
</MockProviders>,
);
expect(screen.getByText(set.name)).toBeDefined();
const reps = RegExp(set.reps.toString());
expect(screen.getByText(reps)).toBeDefined();
const weight = RegExp(set.weight.toString());
expect(screen.getByText(weight)).toBeDefined();
});

View File

@ -0,0 +1,15 @@
import {render, screen} from '@testing-library/react-native';
import React from 'react';
import 'react-native';
import {MockProviders} from '../mock-providers';
import SetList from '../SetList';
it('renders correctly', () => {
render(
<MockProviders>
<SetList />
</MockProviders>,
);
expect(screen.getByText('Home')).toBeDefined();
expect(screen.getByPlaceholderText('Search')).toBeDefined();
});

View File

@ -0,0 +1,27 @@
import {render, screen} from '@testing-library/react-native';
import React from 'react';
import 'react-native';
import {MockProviders} from '../mock-providers';
import Set from '../set';
import WorkoutItem from '../WorkoutItem';
const set: Set = {
name: 'Bench press',
reps: 6,
weight: 20,
seconds: 40,
minutes: 3,
sets: 5,
};
it('renders correctly', () => {
const onRemove = jest.fn();
render(
<MockProviders>
<WorkoutItem item={set} onRemove={onRemove} />
</MockProviders>,
);
expect(screen.getByText(set.name)).toBeDefined();
const sets = RegExp(set.sets?.toString() || '');
expect(screen.getByText(sets)).toBeDefined();
});

View File

@ -0,0 +1,15 @@
import {render, screen} from '@testing-library/react-native';
import React from 'react';
import 'react-native';
import {MockProviders} from '../mock-providers';
import WorkoutList from '../WorkoutList';
it('renders correctly', () => {
render(
<MockProviders>
<WorkoutList />
</MockProviders>,
);
expect(screen.getByText('Workouts')).toBeDefined();
expect(screen.getByPlaceholderText('Search')).toBeDefined();
});

View File

@ -1,6 +1,9 @@
#!/bin/sh #!/bin/sh
set -ex set -ex
yarn tsc
yarn lint
yarn test
git push origin HEAD > /dev/null & git push origin HEAD > /dev/null &
cd android || exit 1 cd android || exit 1
build=app/build.gradle build=app/build.gradle

View File

@ -7,5 +7,8 @@ module.exports = {
transformIgnorePatterns: [ transformIgnorePatterns: [
'node_modules/(?!(jest-)?@?react-native|@react-native-community|@react-navigation)', 'node_modules/(?!(jest-)?@?react-native|@react-native-community|@react-navigation)',
], ],
setupFiles: ['./jestSetup.ts'], setupFiles: [
'./node_modules/react-native-gesture-handler/jestSetup',
'./jestSetup.ts',
],
}; };

View File

@ -1,21 +1,29 @@
import 'react-native-gesture-handler/jestSetup';
import {NativeModules as RNNativeModules} from 'react-native'; import {NativeModules as RNNativeModules} from 'react-native';
RNNativeModules.UIManager = RNNativeModules.UIManager || {}; //RNNativeModules.UIManager = RNNativeModules.UIManager || {};
RNNativeModules.UIManager.RCTView = RNNativeModules.UIManager.RCTView || {}; //RNNativeModules.UIManager.RCTView = RNNativeModules.UIManager.RCTView || {};
RNNativeModules.RNGestureHandlerModule = //RNNativeModules.RNGestureHandlerModule =
RNNativeModules.RNGestureHandlerModule || { // RNNativeModules.RNGestureHandlerModule || {
State: {BEGAN: 'BEGAN', FAILED: 'FAILED', ACTIVE: 'ACTIVE', END: 'END'}, // State: {BEGAN: 'BEGAN', FAILED: 'FAILED', ACTIVE: 'ACTIVE', END: 'END'},
attachGestureHandler: jest.fn(), // attachGestureHandler: jest.fn(),
createGestureHandler: jest.fn(), // createGestureHandler: jest.fn(),
dropGestureHandler: jest.fn(), // dropGestureHandler: jest.fn(),
updateGestureHandler: jest.fn(), // updateGestureHandler: jest.fn(),
}; // };
RNNativeModules.PlatformConstants = RNNativeModules.PlatformConstants || { //RNNativeModules.PlatformConstants = RNNativeModules.PlatformConstants || {
forceTouchAvailable: false, // forceTouchAvailable: false,
}; //};
RNNativeModules.RNViewShot = RNNativeModules.RNViewShot || { RNNativeModules.RNViewShot = RNNativeModules.RNViewShot || {
captureScreen: jest.fn(), captureScreen: jest.fn(),
}; };
jest.mock('react-native-file-access', () => jest.fn()); jest.mock('react-native-file-access', () => jest.fn());
jest.mock('react-native-share', () => jest.fn()); jest.mock('react-native-share', () => jest.fn());
jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');
jest.useFakeTimers();
jest.mock('react-native-reanimated', () => {
const Reanimated = require('react-native-reanimated/mock');
Reanimated.default.call = () => {};
return Reanimated;
});

29
mock-providers.tsx Normal file
View File

@ -0,0 +1,29 @@
import {NavigationContainer} from '@react-navigation/native';
import React from 'react';
import {Provider as PaperProvider} from 'react-native-paper';
import {Color} from './color';
import {lightColors} from './colors';
import MassiveSnack from './MassiveSnack';
import {defaultSettings, SettingsContext} from './use-settings';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
const color = lightColors[0].hex;
export const setColor = jest.fn();
const settings = defaultSettings;
export const setSettings = jest.fn();
export const MockProviders = ({
children,
}: {
children: JSX.Element | JSX.Element[];
}) => (
<Color.Provider value={{color, setColor}}>
<PaperProvider settings={{icon: props => <MaterialIcon {...props} />}}>
<SettingsContext.Provider value={{settings, setSettings}}>
<MassiveSnack>
<NavigationContainer>{children}</NavigationContainer>
</MassiveSnack>
</SettingsContext.Provider>
</PaperProvider>
</Color.Provider>
);

View File

@ -1,24 +1,27 @@
import React, {useContext} from 'react'; import React, {useContext} from 'react';
import Settings from './settings'; import Settings from './settings';
export const defaultSettings: Settings = {
alarm: 0,
color: '',
date: '',
images: 1,
notify: 0,
showDate: 0,
showSets: 1,
showUnit: 1,
sound: '',
steps: 0,
theme: 'system',
vibrate: 1,
noSound: 0,
};
export const SettingsContext = React.createContext<{ export const SettingsContext = React.createContext<{
settings: Settings; settings: Settings;
setSettings: (value: Settings) => void; setSettings: (value: Settings) => void;
}>({ }>({
settings: { settings: defaultSettings,
alarm: 0,
color: '',
date: '',
images: 1,
notify: 0,
showDate: 0,
showSets: 1,
showUnit: 1,
sound: '',
steps: 0,
theme: 'system',
vibrate: 1,
},
setSettings: () => null, setSettings: () => null,
}); });

10689
yarn.lock Normal file

File diff suppressed because it is too large Load Diff