Compare commits

...

18 Commits

Author SHA1 Message Date
MariaZ bee6b145ed Add test checking if StatsTable is shown on button click 2022-09-20 13:42:21 +02:00
MariaZ 57c8e31a9a Fix image import mocking 2022-09-20 13:36:05 +02:00
MariaZ 726b65914b Move react-i18next mock to jest-setup.js 2022-09-20 12:37:41 +02:00
MariaZ 82b6a6b603 Move jest-setup.js to ./test folder 2022-09-20 12:37:13 +02:00
MariaZ 5f61f37d2f Add tests for Stats component 2022-09-20 12:31:54 +02:00
MariaZ 1b6d21f730 Add check for undefined & null data in Stats component 2022-09-20 12:31:27 +02:00
MariaZ be0c11abfe Add tests for StatsTabe component 2022-09-20 11:58:34 +02:00
MariaZ e2e320927b Add tests for StatsOverview component 2022-09-20 10:59:28 +02:00
MariaZ 5c2a80c5c2 Add tests for AppHelp component 2022-09-20 10:48:31 +02:00
MariaZ 2e060d3261 Recreate components folder structure for tests 2022-09-20 10:35:28 +02:00
MariaZ c267e80424 Remove unnecessary import of labels 2022-09-20 10:29:40 +02:00
MariaZ c95e25a9b2 Remove onClose required prop from AppHelp 2022-09-19 21:31:35 +02:00
MariaZ 6c1ee3e5e2 Add explainer text to stats page and move more stats to modal 2022-09-19 21:31:17 +02:00
MariaZ 499e2d0628 Add en translations for stats page 2022-09-19 21:30:51 +02:00
MariaZ 951fb778d4 Add * to cell with standard deviation 2022-09-19 21:23:03 +02:00
MariaZ 6d6473ca78 Add translations to StatsTable 2022-09-19 21:17:09 +02:00
MariaZ 5a0321c5e5 Update styling & add onClose to StatsTable to make it modal 2022-09-19 21:14:06 +02:00
MariaZ 0c6c706274 Add AppHelp component 2022-09-19 20:52:44 +02:00
21 changed files with 1561 additions and 115 deletions
+32
View File
@@ -0,0 +1,32 @@
import React from 'react'
import PropTypes from 'prop-types'
import { StyleSheet, View } from 'react-native'
import AppText from '../common/app-text'
import { Containers, Spacing, Typography } from '../../styles'
const AppHelp = ({ text }) => (
<View style={styles.container}>
<AppText style={styles.accentPurple}>*</AppText>
<AppText>{text}</AppText>
</View>
)
AppHelp.propTypes = {
text: PropTypes.string.isRequired,
}
const styles = StyleSheet.create({
accentPurple: {
...Typography.accentPurple,
alignSelf: 'flex-start',
paddingRight: Spacing.base,
},
container: {
...Containers.rowContainer,
padding: Spacing.base,
},
})
export default AppHelp
+10 -3
View File
@@ -15,10 +15,12 @@ StatsOverview.propTypes = {
} }
const Row = ({ rowContent }) => { const Row = ({ rowContent }) => {
const showHelp = rowContent[1].includes('deviation') ? true : false
return ( return (
<View style={styles.row}> <View style={styles.row}>
<Cell content={rowContent[0]} isLeft /> <Cell content={rowContent[0]} isLeft />
<Cell content={rowContent[1]} /> <Cell content={rowContent[1]} showHelp={showHelp} />
</View> </View>
) )
} }
@@ -27,7 +29,7 @@ Row.propTypes = {
rowContent: PropTypes.array.isRequired, rowContent: PropTypes.array.isRequired,
} }
const Cell = ({ content, isLeft }) => { const Cell = ({ content, isLeft, showHelp }) => {
const styleContainer = isLeft ? styles.cellLeft : styles.cellRight const styleContainer = isLeft ? styles.cellLeft : styles.cellRight
const styleText = isLeft ? styles.accentPurpleBig : styles.accentOrange const styleText = isLeft ? styles.accentPurpleBig : styles.accentOrange
const numberOfLines = isLeft ? 1 : 2 const numberOfLines = isLeft ? 1 : 2
@@ -42,6 +44,7 @@ const Cell = ({ content, isLeft }) => {
> >
{content} {content}
</AppText> </AppText>
{showHelp && <AppText style={styles.accentPurpleBig}>*</AppText>}
</View> </View>
) )
} }
@@ -49,6 +52,7 @@ const Cell = ({ content, isLeft }) => {
Cell.propTypes = { Cell.propTypes = {
content: PropTypes.node.isRequired, content: PropTypes.node.isRequired,
isLeft: PropTypes.bool, isLeft: PropTypes.bool,
showHelp: PropTypes.bool,
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@@ -66,7 +70,10 @@ const styles = StyleSheet.create({
flex: 3, flex: 3,
justifyContent: 'center', justifyContent: 'center',
}, },
cellRight: { flex: 5 }, cellRight: {
flex: 5,
flexDirection: 'row',
},
row: { flexDirection: 'row' }, row: { flexDirection: 'row' },
}) })
+48 -19
View File
@@ -1,12 +1,13 @@
import React from 'react' import React from 'react'
import { FlatList, StyleSheet, View } from 'react-native' import { Dimensions, FlatList, StyleSheet, View } from 'react-native'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import AppText from './app-text' import AppText from './app-text'
import CloseIcon from './close-icon'
import cycleModule from '../../lib/cycle' import cycleModule from '../../lib/cycle'
import { Spacing, Typography, Colors } from '../../styles' import { Sizes, Spacing, Typography, Colors } from '../../styles'
import { humanizeDate } from '../helpers/format-date' import { humanizeDate } from '../helpers/format-date'
const Item = ({ data }) => { const Item = ({ data }) => {
@@ -35,13 +36,17 @@ Item.propTypes = {
data: PropTypes.object.isRequired, data: PropTypes.object.isRequired,
} }
const StatsTable = () => { const StatsTable = ({ onClose }) => {
const renderItem = ({ item }) => <Item data={item} /> const renderItem = ({ item }) => <Item data={item} />
const data = cycleModule().getStats() const data = cycleModule().getStats()
if (!data || data.length === 0) return false if (!data || data.length === 0) return false
return ( return (
<View style={styles.modalContainer}>
<View style={styles.headerContainer}>
<CloseIcon onClose={onClose} />
</View>
<FlatList <FlatList
data={data} data={data}
renderItem={renderItem} renderItem={renderItem}
@@ -52,26 +57,46 @@ const StatsTable = () => {
stickyHeaderIndices={[0]} stickyHeaderIndices={[0]}
contentContainerStyle={styles.container} contentContainerStyle={styles.container}
/> />
</View>
) )
} }
StatsTable.propTypes = {
onClose: PropTypes.func,
}
const ItemDivider = () => <View style={styles.divider} /> const ItemDivider = () => <View style={styles.divider} />
const FlatListHeader = () => ( const FlatListHeader = () => {
const { t } = useTranslation(null, { keyPrefix: 'stats' })
return (
<View style={styles.row}> <View style={styles.row}>
<View style={styles.accentCell}> <View style={styles.accentCell}>
<AppText style={styles.header}>{'Cycle Start'}</AppText> <AppText style={styles.header}>{t('cycle_start')}</AppText>
</View> </View>
<View style={styles.cell}> <View style={styles.cell}>
<AppText style={styles.header}>{'Cycle Length'}</AppText> <AppText style={styles.header}>{t('cycle_length')}</AppText>
</View> </View>
<View style={styles.cell}> <View style={styles.cell}>
<AppText style={styles.header}>{'Bleeding'}</AppText> <AppText style={styles.header}>{t('bleeding')}</AppText>
</View> </View>
</View> </View>
) )
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
accentCell: {
flex: 3,
justifyContent: 'center',
},
cell: {
flex: 2,
justifyContent: 'center',
},
container: {
paddingHorizontal: Spacing.base,
},
divider: { divider: {
height: 1, height: 1,
width: '100%', width: '100%',
@@ -81,27 +106,31 @@ const styles = StyleSheet.create({
...Typography.accentOrange, ...Typography.accentOrange,
paddingVertical: Spacing.small, paddingVertical: Spacing.small,
}, },
headerContainer: {
flexDirection: 'row',
justifyContent: 'flex-end',
paddingTop: Spacing.base,
paddingRight: Spacing.base,
},
headerDivider: { headerDivider: {
borderBottomColor: Colors.purple, borderBottomColor: Colors.purple,
borderBottomWidth: 2, borderBottomWidth: 2,
}, },
modalContainer: {
alignSelf: 'center',
backgroundColor: Colors.turquoiseLight,
marginTop: Sizes.huge * 2,
maxHeight: Dimensions.get('window').height * 0.7,
minHeight: '40%',
position: 'absolute',
width: '100%',
},
row: { row: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
paddingVertical: Spacing.tiny, paddingVertical: Spacing.tiny,
backgroundColor: Colors.turquoiseLight, backgroundColor: Colors.turquoiseLight,
}, },
cell: {
flex: 2,
justifyContent: 'center',
},
accentCell: {
flex: 3,
justifyContent: 'center',
},
container: {
paddingHorizontal: Spacing.base,
},
}) })
export default StatsTable export default StatsTable
+41 -21
View File
@@ -1,39 +1,50 @@
import React from 'react' import React, { useState } from 'react'
import { ImageBackground, View } from 'react-native' import { ImageBackground, SafeAreaView, ScrollView, View } from 'react-native'
import { ScaledSheet } from 'react-native-size-matters' import { ScaledSheet } from 'react-native-size-matters'
import { useTranslation } from 'react-i18next'
import Button from './common/button'
import AppHelp from './common/AppHelp'
import AppModal from './common/app-modal'
import AppText from './common/app-text' import AppText from './common/app-text'
import StatsOverview from './common/StatsOverview' import StatsOverview from './common/StatsOverview'
import StatsTable from './common/StatsTable' import StatsTable from './common/StatsTable'
import cycleModule from '../lib/cycle' import cycleModule from '../lib/cycle'
import { getCycleLengthStats as getCycleInfo } from '../lib/cycle-length' import { getCycleLengthStats as getCycleInfo } from '../lib/cycle-length'
import { stats as labels } from '../i18n/en/labels'
import { Containers, Sizes, Spacing, Typography } from '../styles' import { Containers, Sizes, Spacing, Typography } from '../styles'
const image = require('../assets/cycle-icon.png') const image = require('../assets/cycle-icon.png')
const Stats = () => { const Stats = () => {
const [isStatsVisible, setIsStatsVisible] = useState(false)
const { t } = useTranslation(null, { keyPrefix: 'stats' })
const cycleLengths = cycleModule().getAllCycleLengths() const cycleLengths = cycleModule().getAllCycleLengths()
const numberOfCycles = cycleLengths.length const numberOfCycles = cycleLengths?.length
const hasAtLeastOneCycle = numberOfCycles >= 1 const hasAtLeastOneCycle = numberOfCycles >= 1
const cycleData = hasAtLeastOneCycle const cycleData = hasAtLeastOneCycle
? getCycleInfo(cycleLengths) ? getCycleInfo(cycleLengths)
: { minimum: '—', maximum: '—', stdDeviation: '—' } : { minimum: '—', maximum: '—', stdDeviation: '—' }
const statsData = [ const statsData = [
[cycleData.minimum, labels.minLabel], [cycleData.minimum, t('min')],
[cycleData.maximum, labels.maxLabel], [cycleData.maximum, t('max')],
[cycleData.stdDeviation ? cycleData.stdDeviation : '—', labels.stdLabel], [
[numberOfCycles, labels.basisOfStatsEnd], cycleData.stdDeviation ? cycleData.stdDeviation : '—',
t('standard_deviation'),
],
[numberOfCycles, t('completed_cycles')],
] ]
return ( return (
<View style={styles.pageContainer}> <SafeAreaView style={styles.pageContainer}>
<View style={styles.overviewContainer}> <ScrollView contentContainerStyle={styles.overviewContainer}>
<AppText>{labels.cycleLengthExplainer}</AppText> <AppText>{t('cycle_length_explainer')}</AppText>
{!hasAtLeastOneCycle && <AppText>{labels.emptyStats}</AppText>} {!hasAtLeastOneCycle && <AppText>{t('no_data')}</AppText>}
{hasAtLeastOneCycle && ( {hasAtLeastOneCycle && (
<>
<View style={styles.container}> <View style={styles.container}>
<View style={styles.columnLeft}> <View style={styles.columnLeft}>
<ImageBackground <ImageBackground
@@ -48,22 +59,31 @@ const Stats = () => {
> >
{cycleData.mean} {cycleData.mean}
</AppText> </AppText>
<AppText style={styles.accentPurpleHuge}> <AppText style={styles.accentPurpleHuge}>{t('days')}</AppText>
{labels.daysLabel}
</AppText>
</ImageBackground> </ImageBackground>
<AppText style={styles.accentOrange}> <AppText style={styles.accentOrange}>{t('average')}</AppText>
{labels.averageLabel}
</AppText>
</View> </View>
<View style={styles.columnRight}> <View style={styles.columnRight}>
<StatsOverview data={statsData} /> <StatsOverview data={statsData} />
</View> </View>
</View> </View>
<Button isCTA onPress={() => setIsStatsVisible(true)}>
{t('show_stats')}
</Button>
<AppHelp text={t('standard_deviation_help')} />
</>
)} )}
</View> </ScrollView>
<StatsTable />
</View> {isStatsVisible && (
<AppModal onClose={() => setIsStatsVisible(false)}>
<StatsTable
onClose={() => setIsStatsVisible(false)}
testID="statsTable"
/>
</AppModal>
)}
</SafeAreaView>
) )
} }
+15
View File
@@ -64,5 +64,20 @@
"plurals": { "plurals": {
"day": "{{count}} day", "day": "{{count}} day",
"day_plural": "{{count}} days" "day_plural": "{{count}} days"
},
"stats": {
"show_stats": "Show period details",
"cycle_start": "Cycle start",
"cycle_length": "Cycle length",
"bleeding": "Bleeding",
"cycle_length_explainer": "Basic statistics about the length of your cycles.",
"no_data": "At least one completed cycle is needed to display stats.",
"days": "days",
"completed_cycles": "completed\ncycles",
"average": "Average cycle",
"min": "Shortest",
"max": "Longest",
"standard_deviation": "Standard\ndeviation",
"standard_deviation_help": "Based on the standard deviation of all your tracked periods drip. calculates a range for the starting day of the upcoming 3 periods. The range will be 3 days if your standard deviation is smaller than 1.5 and 5 days if the value is bigger.\n\nThe standard deviation tells you how much the length of your periods vary, 0 means all your periods are exactly the same length and the bigger the value the more the period length varies."
} }
} }
-2
View File
@@ -1,2 +0,0 @@
// Import Jest Native matchers
import '@testing-library/jest-native/extend-expect'
+4 -1
View File
@@ -1,7 +1,10 @@
module.exports = { module.exports = {
preset: '@testing-library/react-native', preset: '@testing-library/react-native',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
setupFilesAfterEnv: ['./jest-setup.js'], moduleNameMapper: {
'\\.(png)$': require.resolve('./test/file-mock.js'),
},
setupFilesAfterEnv: ['./test/jest-setup.js'],
transformIgnorePatterns: [ transformIgnorePatterns: [
'node_modules/(?!((jest-)?react-native(-.*)?|@react-native(-community)?)/)', 'node_modules/(?!((jest-)?react-native(-.*)?|@react-native(-community)?)/)',
], ],
+19 -15
View File
@@ -6,7 +6,7 @@ import Spacing from './spacing'
export const fonts = { export const fonts = {
main: Platform.OS === 'ios' ? 'Jost-Book' : 'Jost-400-Book', main: Platform.OS === 'ios' ? 'Jost-Book' : 'Jost-400-Book',
bold : Platform.OS === 'ios' ? 'Jost-Bold' : 'Jost-700-Bold', bold: Platform.OS === 'ios' ? 'Jost-Bold' : 'Jost-700-Bold',
} }
export const sizes = { export const sizes = {
@@ -23,7 +23,7 @@ export const sizes = {
const accentText = { const accentText = {
fontFamily: fonts.bold, fontFamily: fonts.bold,
textAlignVertical: 'center', textAlignVertical: 'center',
textTransform: 'uppercase' textTransform: 'uppercase',
} }
const accentTextBig = { const accentTextBig = {
@@ -43,47 +43,51 @@ const accentTextHuge = {
const accentTextSmall = { const accentTextSmall = {
...accentText, ...accentText,
fontSize: sizes.small fontSize: sizes.small,
} }
const title = { const title = {
color: Colors.purple, color: Colors.purple,
marginVertical: Spacing.large marginVertical: Spacing.large,
} }
const label = { const label = {
fontSize: sizes.small, fontSize: sizes.small,
textTransform: 'uppercase' textTransform: 'uppercase',
} }
export default { export default {
accentOrange: { accentOrange: {
...accentTextSmall, ...accentTextSmall,
color: Colors.orange color: Colors.orange,
},
accentPurple: {
...accentTextSmall,
color: Colors.purple,
}, },
accentPurpleBig: { accentPurpleBig: {
...accentTextBig, ...accentTextBig,
color: Colors.purple color: Colors.purple,
}, },
accentPurpleGiant: { accentPurpleGiant: {
...accentTextGiant, ...accentTextGiant,
color: Colors.purple color: Colors.purple,
}, },
accentPurpleHuge: { accentPurpleHuge: {
...accentTextHuge, ...accentTextHuge,
color: Colors.purple color: Colors.purple,
}, },
mainText: { mainText: {
fontFamily: fonts.main, fontFamily: fonts.main,
fontSize: sizes.base fontSize: sizes.base,
}, },
label: { label: {
...label ...label,
}, },
labelBold: { labelBold: {
color: Colors.greyDark, color: Colors.greyDark,
fontWeight: 'bold', fontWeight: 'bold',
...label ...label,
}, },
labelLight: { labelLight: {
color: Colors.grey, color: Colors.grey,
@@ -91,7 +95,7 @@ export default {
}, },
subtitle: { subtitle: {
fontSize: sizes.subtitle, fontSize: sizes.subtitle,
...title ...title,
}, },
title: { title: {
alignSelf: 'center', alignSelf: 'center',
@@ -99,7 +103,7 @@ export default {
fontWeight: '700', fontWeight: '700',
fontSize: sizes.title, fontSize: sizes.title,
marginHorizontal: Spacing.base, marginHorizontal: Spacing.base,
...title ...title,
}, },
titleWithoutMargin: { titleWithoutMargin: {
alignSelf: 'center', alignSelf: 'center',
@@ -107,5 +111,5 @@ export default {
fontFamily: fonts.bold, fontFamily: fonts.bold,
fontWeight: '700', fontWeight: '700',
fontSize: sizes.title, fontSize: sizes.title,
} },
} }
@@ -1,21 +1,14 @@
import React from 'react' import React from 'react'
import { render, screen, fireEvent } from '@testing-library/react-native' import { render, screen, fireEvent } from '@testing-library/react-native'
import AcceptLicense from '../components/AcceptLicense'
import { saveLicenseFlag } from '../local-storage' import AcceptLicense from '../../components/AcceptLicense'
jest.mock('../local-storage', () => ({ import { saveLicenseFlag } from '../../local-storage'
jest.mock('../../local-storage', () => ({
saveLicenseFlag: jest.fn(() => Promise.resolve()), saveLicenseFlag: jest.fn(() => Promise.resolve()),
})) }))
jest.mock('react-i18next', () => ({
useTranslation: () => ({
t: (str, options) => {
return str + (options ? JSON.stringify(options) : '')
},
}),
}))
describe('AcceptLicense', () => { describe('AcceptLicense', () => {
test('On clicking OK button, the license is accepted', async () => { test('On clicking OK button, the license is accepted', async () => {
const mockedSetLicense = jest.fn() const mockedSetLicense = jest.fn()
@@ -0,0 +1,395 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Stats screen when provided data, renders stats 1`] = `
<RCTSafeAreaView
emulateUnlessSupported={true}
style={
Object {
"backgroundColor": "#E9F2ED",
"flex": 1,
}
}
>
<RCTScrollView
contentContainerStyle={
Object {
"paddingHorizontal": 34.285714285714285,
"paddingTop": 34.285714285714285,
}
}
>
<View>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
cycle_length_explainer
</Text>
<View
style={
Object {
"alignItems": "center",
"flexDirection": "row",
"justifyContent": "space-between",
}
}
>
<View
style={
Object {
"flex": 3,
"flexDirection": "column",
}
}
>
<View
accessibilityIgnoresInvertColors={true}
style={
Object {
"marginBottom": 42.857142857142854,
"paddingTop": 107.14285714285714,
}
}
>
<Image
source={123}
style={
Array [
Object {
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
},
Object {
"height": undefined,
"width": undefined,
},
Object {
"resizeMode": "contain",
},
]
}
/>
<Text
ellipsizeMode="clip"
numberOfLines={1}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 85.71428571428571,
"marginTop": -68.57142857142857,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
30.33
</Text>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 68.57142857142857,
"marginTop": -34.285714285714285,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
days
</Text>
</View>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
average
</Text>
</View>
<View
style={
Object {
"flex": 5,
"flexDirection": "column",
"paddingTop": 21.428571428571427,
}
}
>
<StatsOverview
data={
Array [
Array [
30,
"min",
],
Array [
31,
"max",
],
Array [
0.58,
"standard_deviation",
],
Array [
3,
"completed_cycles",
],
]
}
/>
</View>
</View>
<View
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"alignItems": "center",
"alignSelf": "center",
"backgroundColor": "#F38337",
"borderRadius": 25,
"flexDirection": "row",
"justifyContent": "center",
"marginTop": 34.285714285714285,
"minWidth": "15%",
"opacity": 1,
"paddingHorizontal": 8.571428571428571,
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Array [
Object {
"color": "white",
"fontFamily": "Jost-Bold",
},
Object {
"fontSize": 27.857142857142858,
"padding": 21.428571428571427,
"textTransform": "uppercase",
},
],
]
}
>
show_stats
</Text>
</View>
<AppHelp
text="standard_deviation_help"
/>
</View>
</RCTScrollView>
</RCTSafeAreaView>
`;
exports[`Stats screen when provided no data, renders no_data text 1`] = `
<RCTSafeAreaView
emulateUnlessSupported={true}
style={
Object {
"backgroundColor": "#E9F2ED",
"flex": 1,
}
}
>
<RCTScrollView
contentContainerStyle={
Object {
"paddingHorizontal": 34.285714285714285,
"paddingTop": 34.285714285714285,
}
}
>
<View>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
cycle_length_explainer
</Text>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
no_data
</Text>
</View>
</RCTScrollView>
</RCTSafeAreaView>
`;
exports[`Stats screen when provided null, renders no_data text 1`] = `
<RCTSafeAreaView
emulateUnlessSupported={true}
style={
Object {
"backgroundColor": "#E9F2ED",
"flex": 1,
}
}
>
<RCTScrollView
contentContainerStyle={
Object {
"paddingHorizontal": 34.285714285714285,
"paddingTop": 34.285714285714285,
}
}
>
<View>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
cycle_length_explainer
</Text>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
no_data
</Text>
</View>
</RCTScrollView>
</RCTSafeAreaView>
`;
exports[`Stats screen when provided undefined, renders no_data text 1`] = `
<RCTSafeAreaView
emulateUnlessSupported={true}
style={
Object {
"backgroundColor": "#E9F2ED",
"flex": 1,
}
}
>
<RCTScrollView
contentContainerStyle={
Object {
"paddingHorizontal": 34.285714285714285,
"paddingTop": 34.285714285714285,
}
}
>
<View>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
cycle_length_explainer
</Text>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
no_data
</Text>
</View>
</RCTScrollView>
</RCTSafeAreaView>
`;
+13
View File
@@ -0,0 +1,13 @@
import React from 'react'
import { render } from '@testing-library/react-native'
import AppHelp from '../../../components/common/AppHelp'
describe('AppHelp screen', () => {
test('when provided text, should render it', async () => {
const text = 'Some help test'
const { toJSON } = render(<AppHelp text={text} />)
expect(toJSON()).toMatchSnapshot()
})
})
@@ -0,0 +1,25 @@
import React from 'react'
import { render } from '@testing-library/react-native'
import StatsOverview from '../../../components/common/StatsOverview'
describe('StatsOverview screen', () => {
test('when provided correct, renders it', async () => {
const data = [
[21, 'shortest'],
[21, 'longest'],
[0, 'standard deviation'],
[2, 'completed cycles'],
]
const { toJSON } = render(<StatsOverview data={data} />)
expect(toJSON()).toMatchSnapshot()
})
test('when provided empty data, renders nothing (does not break)', async () => {
const data = []
const { toJSON } = render(<StatsOverview data={data} />)
expect(toJSON()).toMatchSnapshot()
})
})
+47
View File
@@ -0,0 +1,47 @@
import React from 'react'
import { render } from '@testing-library/react-native'
import StatsTable from '../../../components/common/StatsTable'
const mockGetStats = jest
.fn()
.mockImplementationOnce(() => [
{ date: '2022-07-01', cycleLength: 31, bleedingLength: 5 },
{ date: '2022-06-01', cycleLength: 31, bleedingLength: 5 },
])
.mockImplementationOnce(() => [])
.mockImplementationOnce(() => null)
.mockImplementationOnce(() => undefined)
jest.mock('../../../lib/cycle', () => ({
__esModule: true,
default: () => ({
getStats: mockGetStats,
}),
}))
describe('StatsTable screen', () => {
test('when provided correct data set, renders it', async () => {
const { toJSON } = render(<StatsTable onClose={jest.fn()} />)
expect(toJSON()).toMatchSnapshot()
})
test('when provided no data, renders nothing', async () => {
const { toJSON } = render(<StatsTable onClose={jest.fn()} />)
expect(toJSON()).toMatchSnapshot()
})
test('when provided null, renders nothing', async () => {
const { toJSON } = render(<StatsTable onClose={jest.fn()} />)
expect(toJSON()).toMatchSnapshot()
})
test('when provided undefined, renders nothing', async () => {
const { toJSON } = render(<StatsTable onClose={jest.fn()} />)
expect(toJSON()).toMatchSnapshot()
})
})
@@ -0,0 +1,51 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AppHelp screen when provided text, should render it 1`] = `
<View
style={
Object {
"alignItems": "center",
"flexDirection": "row",
"justifyContent": "space-between",
"padding": 34.285714285714285,
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"alignSelf": "flex-start",
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"paddingRight": 34.285714285714285,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
*
</Text>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
Some help test
</Text>
</View>
`;
@@ -0,0 +1,321 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`StatsOverview screen when provided correct, renders it 1`] = `
Array [
<View
style={
Object {
"flexDirection": "row",
}
}
>
<View
style={
Object {
"alignItems": "flex-end",
"flex": 3,
"justifyContent": "center",
}
}
>
<Text
ellipsizeMode="clip"
numberOfLines={1}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 64.28571428571428,
"marginRight": 8.571428571428571,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
21
</Text>
</View>
<View
style={
Object {
"flex": 5,
"flexDirection": "row",
}
}
>
<Text
ellipsizeMode="tail"
numberOfLines={2}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"margin": 15,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
shortest
</Text>
</View>
</View>,
<View
style={
Object {
"flexDirection": "row",
}
}
>
<View
style={
Object {
"alignItems": "flex-end",
"flex": 3,
"justifyContent": "center",
}
}
>
<Text
ellipsizeMode="clip"
numberOfLines={1}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 64.28571428571428,
"marginRight": 8.571428571428571,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
21
</Text>
</View>
<View
style={
Object {
"flex": 5,
"flexDirection": "row",
}
}
>
<Text
ellipsizeMode="tail"
numberOfLines={2}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"margin": 15,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
longest
</Text>
</View>
</View>,
<View
style={
Object {
"flexDirection": "row",
}
}
>
<View
style={
Object {
"alignItems": "flex-end",
"flex": 3,
"justifyContent": "center",
}
}
>
<Text
ellipsizeMode="clip"
numberOfLines={1}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 64.28571428571428,
"marginRight": 8.571428571428571,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
0
</Text>
</View>
<View
style={
Object {
"flex": 5,
"flexDirection": "row",
}
}
>
<Text
ellipsizeMode="tail"
numberOfLines={2}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"margin": 15,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
standard deviation
</Text>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 64.28571428571428,
"marginRight": 8.571428571428571,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
*
</Text>
</View>
</View>,
<View
style={
Object {
"flexDirection": "row",
}
}
>
<View
style={
Object {
"alignItems": "flex-end",
"flex": 3,
"justifyContent": "center",
}
}
>
<Text
ellipsizeMode="clip"
numberOfLines={1}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#3A2671",
"fontFamily": "Jost-Bold",
"fontSize": 64.28571428571428,
"marginRight": 8.571428571428571,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
2
</Text>
</View>
<View
style={
Object {
"flex": 5,
"flexDirection": "row",
}
}
>
<Text
ellipsizeMode="tail"
numberOfLines={2}
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"margin": 15,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
completed cycles
</Text>
</View>
</View>,
]
`;
exports[`StatsOverview screen when provided empty data, renders nothing (does not break) 1`] = `null`;
@@ -0,0 +1,433 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`StatsTable screen when provided correct data set, renders it 1`] = `
<View
style={
Object {
"alignSelf": "center",
"backgroundColor": "#E9F2ED",
"marginTop": 137.14285714285714,
"maxHeight": 933.8,
"minHeight": "40%",
"position": "absolute",
"width": "100%",
}
}
>
<View
style={
Object {
"flexDirection": "row",
"justifyContent": "flex-end",
"paddingRight": 34.285714285714285,
"paddingTop": 34.285714285714285,
}
}
>
<View
accessible={true}
collapsable={false}
focusable={true}
hitSlop={
Object {
"bottom": 39.23529411764706,
"left": 42.857142857142854,
"right": 42.857142857142854,
"top": 39.23529411764706,
}
}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"alignSelf": "flex-start",
"marginBottom": 34.285714285714285,
"opacity": 1,
}
}
>
<Text
allowFontScaling={false}
selectable={false}
style={
Array [
Object {
"color": undefined,
"fontSize": 12,
},
Array [
Object {
"fontSize": 47.14285714285714,
},
undefined,
Object {
"color": "#F38337",
},
],
Object {
"fontFamily": "Entypo",
"fontStyle": "normal",
"fontWeight": "normal",
},
Object {},
]
}
>
</Text>
</View>
</View>
<RCTScrollView
ItemSeparatorComponent={[Function]}
ListHeaderComponent={[Function]}
ListHeaderComponentStyle={
Object {
"borderBottomColor": "#3A2671",
"borderBottomWidth": 2,
}
}
contentContainerStyle={
Object {
"paddingHorizontal": 34.285714285714285,
}
}
data={
Array [
Object {
"bleedingLength": 5,
"cycleLength": 31,
"date": "2022-07-01",
},
Object {
"bleedingLength": 5,
"cycleLength": 31,
"date": "2022-06-01",
},
]
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
onMomentumScrollEnd={[Function]}
onScroll={[Function]}
onScrollBeginDrag={[Function]}
onScrollEndDrag={[Function]}
removeClippedSubviews={false}
renderItem={[Function]}
scrollEventThrottle={50}
stickyHeaderIndices={
Array [
0,
]
}
viewabilityConfigCallbackPairs={Array []}
>
<View>
<View
onLayout={[Function]}
style={
Object {
"borderBottomColor": "#3A2671",
"borderBottomWidth": 2,
}
}
>
<View
style={
Object {
"backgroundColor": "#E9F2ED",
"flexDirection": "row",
"justifyContent": "space-between",
"paddingVertical": 8.571428571428571,
}
}
>
<View
style={
Object {
"flex": 3,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"paddingVertical": 21.428571428571427,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
cycle_start
</Text>
</View>
<View
style={
Object {
"flex": 2,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"paddingVertical": 21.428571428571427,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
cycle_length
</Text>
</View>
<View
style={
Object {
"flex": 2,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
Object {
"color": "#F38337",
"fontFamily": "Jost-Bold",
"fontSize": 27.857142857142858,
"paddingVertical": 21.428571428571427,
"textAlignVertical": "center",
"textTransform": "uppercase",
},
]
}
>
bleeding
</Text>
</View>
</View>
</View>
<View
onLayout={[Function]}
style={null}
>
<View
style={
Object {
"backgroundColor": "#E9F2ED",
"flexDirection": "row",
"justifyContent": "space-between",
"paddingVertical": 8.571428571428571,
}
}
>
<View
style={
Object {
"flex": 3,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
01. Jul 22
</Text>
</View>
<View
style={
Object {
"flex": 2,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
day{"count":31}
</Text>
</View>
<View
style={
Object {
"flex": 2,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
day{"count":5}
</Text>
</View>
</View>
<View
style={
Object {
"backgroundColor": "#888",
"height": 1,
"width": "100%",
}
}
/>
</View>
<View
onLayout={[Function]}
style={null}
>
<View
style={
Object {
"backgroundColor": "#E9F2ED",
"flexDirection": "row",
"justifyContent": "space-between",
"paddingVertical": 8.571428571428571,
}
}
>
<View
style={
Object {
"flex": 3,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
01. Jun 22
</Text>
</View>
<View
style={
Object {
"flex": 2,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
day{"count":31}
</Text>
</View>
<View
style={
Object {
"flex": 2,
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#555",
"fontFamily": "Jost-Book",
"fontSize": 34.285714285714285,
},
undefined,
]
}
>
day{"count":5}
</Text>
</View>
</View>
</View>
</View>
</RCTScrollView>
</View>
`;
exports[`StatsTable screen when provided no data, renders nothing 1`] = `null`;
exports[`StatsTable screen when provided null, renders nothing 1`] = `null`;
exports[`StatsTable screen when provided undefined, renders nothing 1`] = `null`;
@@ -1,14 +1,7 @@
import React from 'react' import React from 'react'
import { render, screen } from '@testing-library/react-native' import { render, screen } from '@testing-library/react-native'
import License from '../components/settings/License'
jest.mock('react-i18next', () => ({ import License from '../../../components/settings/License'
useTranslation: () => ({
t: (str, options) => {
return str + (options ? JSON.stringify(options) : '')
},
}),
}))
describe('License screen', () => { describe('License screen', () => {
test('It should have a correct year', async () => { test('It should have a correct year', async () => {
+58
View File
@@ -0,0 +1,58 @@
import React from 'react'
import { fireEvent, render } from '@testing-library/react-native'
import Stats from '../../components/stats'
jest.mock('../../components/common/AppHelp', () => 'AppHelp')
jest.mock('../../components/common/StatsOverview', () => 'StatsOverview')
jest.mock('../../components/common/StatsTable', () => 'StatsTable')
const mockGetAllCycleLengths = jest
.fn()
.mockImplementationOnce(() => [])
.mockImplementationOnce(() => [30, 31, 30])
.mockImplementationOnce(() => null)
.mockImplementationOnce(() => undefined)
.mockImplementationOnce(() => [30, 31, 30])
jest.mock('../../lib/cycle', () => ({
__esModule: true,
default: () => ({
getAllCycleLengths: mockGetAllCycleLengths,
}),
}))
describe('Stats screen', () => {
test('when provided no data, renders no_data text', async () => {
const { toJSON } = render(<Stats />)
expect(toJSON()).toMatchSnapshot()
})
test('when provided data, renders stats', async () => {
const { toJSON } = render(<Stats />)
expect(toJSON()).toMatchSnapshot()
})
test('when provided null, renders no_data text', async () => {
const { toJSON } = render(<Stats />)
expect(toJSON()).toMatchSnapshot()
})
test('when provided undefined, renders no_data text', async () => {
const { toJSON } = render(<Stats />)
expect(toJSON()).toMatchSnapshot()
})
test('when button is clicked, StatsTable is rendered', async () => {
const { getByText, findByTestId } = render(<Stats />)
const button = getByText('show_stats')
fireEvent(button, 'click')
await expect(findByTestId('statsTable')).toBeTruthy()
})
})
+1
View File
@@ -0,0 +1 @@
module.exports = 123
+8
View File
@@ -0,0 +1,8 @@
// Import Jest Native matchers
import '@testing-library/jest-native/extend-expect'
jest.mock('react-i18next', () => ({
useTranslation: () => ({
t: (str, options) => str + (options ? JSON.stringify(options) : ''),
}),
}))