Unit Testing (#35)
* add testing lib * remove coverage folder from git * finished basic test setup * fix tests typescript and import paths * add first snapshot * testing utils * rename test files; update script flags; ++tests * testing utils functions * testing downloadAndResize wip * remove download test * specify unwanted coverage paths; remove update snapshots flag * fix strings tests * testing downloadAndResize method * increasing testing * fixing snapshots wip * fixed shell mobile snapshot * adding snapshots for the screens * fix onboard snapshot * fix typescript issues * fix TabsSelector snapshot * Account for testing device's locale in ago() tests * Remove platform detection on regex * mocking store state wip * mocking store state * increasing test coverage * increasing test coverage * increasing test coverage on src/screens * src/screens (except for profile) above 80% cov * testing profile screen wip * increase coverage on Menu and TabsSelector * mocking profile ui state wip * mocking profile ui state wip * fixing mobileshell tests wip * snapshots using testing-library * fixing profile tests wip * removing mobile shell tests * src/view/com tests wip * remove unnecessary patch-package * fixed profile test error * clear mocks after every test * fix base mocked store values (getters) * fix base mocked store values (hasLoaded, nonReplyFeed) * profile screen above 80% coverage * testing custom hooks * improving composer coverage * fix tests after merge * finishing composer coverage * improving src/com/discover coverage * improve src/view/com/login coverage fix SuggestedFollows tests adding some comments * fix SuggestedFollows tests * improve src/view/com/profile coverage extra minor fixes * improve src/view/com/notifications coverage * update coverage ignore patterns * rename errorMessageTryAgainButton increase SuggestedFollows converage * improve src/view/com/posts coverage * improve src/view/com/onboard coverage * update snapshot * improve src/view/com/post coverage * improve src/view/com/post-thread coverage rename ErrorMessage tests test Debug and Log components * init testing state * testing root-store * updating comments * small fixes * removed extra console logs * improve src/state/models coverage refactor rootStore rename some spies * adding cleanup method after tests * improve src/state/models coverage * improve src/state/models coverage * improve src/state/models coverage * improve src/state/models coverage * test setInterval in setupState * Clean up tests and update Home screen state management * Remove some tests we dont need * Remove snapshot tests * Remove any tests that dont demonstrate clear value * Cleanup Co-authored-by: Paul Frazee <pfrazee@gmail.com>
This commit is contained in:
parent
11c861d2d3
commit
5abcc8e336
95 changed files with 2852 additions and 9936 deletions
|
@ -25,7 +25,9 @@ export const Contacts = ({navIdx, visible, params}: ScreenParams) => {
|
|||
return (
|
||||
<View>
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.title}>Contacts</Text>
|
||||
<Text testID="contactsTitle" style={styles.title}>
|
||||
Contacts
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.section}>
|
||||
<View style={styles.searchContainer}>
|
||||
|
@ -35,6 +37,7 @@ export const Contacts = ({navIdx, visible, params}: ScreenParams) => {
|
|||
style={styles.searchIcon}
|
||||
/>
|
||||
<TextInput
|
||||
testID="contactsTextInput"
|
||||
ref={inputRef}
|
||||
value={searchText}
|
||||
style={styles.searchInput}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, {useState, useEffect} from 'react'
|
||||
import React, {useEffect} from 'react'
|
||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import useAppState from 'react-native-appstate-hook'
|
||||
|
@ -24,48 +24,48 @@ export const Home = observer(function Home({
|
|||
const store = useStores()
|
||||
const onMainScroll = useOnMainScroll(store)
|
||||
const safeAreaInsets = useSafeAreaInsets()
|
||||
const [hasSetup, setHasSetup] = useState<boolean>(false)
|
||||
const [wasVisible, setWasVisible] = React.useState<boolean>(false)
|
||||
const {appState} = useAppState({
|
||||
onForeground: () => doPoll(true),
|
||||
})
|
||||
|
||||
const doPoll = (knownActive = false) => {
|
||||
if ((!knownActive && appState !== 'active') || !visible) {
|
||||
return
|
||||
}
|
||||
if (store.me.mainFeed.isLoading) {
|
||||
return
|
||||
}
|
||||
store.log.debug('Polling home feed')
|
||||
store.me.mainFeed.checkForLatest().catch(e => {
|
||||
store.log.error('Failed to poll feed', e)
|
||||
})
|
||||
}
|
||||
const doPoll = React.useCallback(
|
||||
(knownActive = false) => {
|
||||
if ((!knownActive && appState !== 'active') || !visible) {
|
||||
return
|
||||
}
|
||||
if (store.me.mainFeed.isLoading) {
|
||||
return
|
||||
}
|
||||
store.log.debug('Polling home feed')
|
||||
store.me.mainFeed.checkForLatest().catch(e => {
|
||||
store.log.error('Failed to poll feed', e)
|
||||
})
|
||||
},
|
||||
[appState, visible, store],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
let aborted = false
|
||||
const pollInterval = setInterval(() => doPoll(), 15e3)
|
||||
if (!visible) {
|
||||
setWasVisible(false)
|
||||
return
|
||||
} else if (wasVisible) {
|
||||
return
|
||||
}
|
||||
setWasVisible(true)
|
||||
|
||||
if (hasSetup) {
|
||||
store.log.debug('Updating home feed')
|
||||
store.nav.setTitle(navIdx, 'Home')
|
||||
store.log.debug('Updating home feed')
|
||||
if (store.me.mainFeed.hasContent) {
|
||||
store.me.mainFeed.update()
|
||||
doPoll()
|
||||
} else {
|
||||
store.nav.setTitle(navIdx, 'Home')
|
||||
store.log.debug('Fetching home feed')
|
||||
store.me.mainFeed.setup().then(() => {
|
||||
if (aborted) return
|
||||
setHasSetup(true)
|
||||
})
|
||||
store.me.mainFeed.setup()
|
||||
}
|
||||
return () => {
|
||||
clearInterval(pollInterval)
|
||||
aborted = true
|
||||
}
|
||||
}, [visible, store])
|
||||
}, [visible, store, navIdx, doPoll, wasVisible])
|
||||
|
||||
const onPressCompose = () => {
|
||||
store.shell.openComposer({})
|
||||
|
@ -82,6 +82,7 @@ export const Home = observer(function Home({
|
|||
<View style={s.flex1}>
|
||||
<ViewHeader title="Bluesky" subtitle="Private Beta" canGoBack={false} />
|
||||
<Feed
|
||||
testID="homeFeed"
|
||||
key="default"
|
||||
feed={store.me.mainFeed}
|
||||
scrollElRef={scrollElRef}
|
||||
|
|
|
@ -35,8 +35,11 @@ const SigninOrCreateAccount = ({
|
|||
<Text style={styles.title}>Bluesky</Text>
|
||||
<Text style={styles.subtitle}>[ private beta ]</Text>
|
||||
</View>
|
||||
<View style={s.flex1}>
|
||||
<TouchableOpacity style={styles.btn} onPress={onPressCreateAccount}>
|
||||
<View testID="signinOrCreateAccount" style={s.flex1}>
|
||||
<TouchableOpacity
|
||||
testID="createAccountButton"
|
||||
style={styles.btn}
|
||||
onPress={onPressCreateAccount}>
|
||||
<Text style={styles.btnLabel}>Create a new account</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.or}>
|
||||
|
@ -60,7 +63,10 @@ const SigninOrCreateAccount = ({
|
|||
</Svg>
|
||||
<Text style={styles.orLabel}>or</Text>
|
||||
</View>
|
||||
<TouchableOpacity style={styles.btn} onPress={onPressSignin}>
|
||||
<TouchableOpacity
|
||||
testID="signInButton"
|
||||
style={styles.btn}
|
||||
onPress={onPressSignin}>
|
||||
<Text style={styles.btnLabel}>Sign in</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
|
|
@ -7,7 +7,7 @@ import {useStores} from '../../state'
|
|||
export const NotFound = () => {
|
||||
const stores = useStores()
|
||||
return (
|
||||
<View>
|
||||
<View testID="notFoundView">
|
||||
<ViewHeader title="Page not found" />
|
||||
<View
|
||||
style={{
|
||||
|
@ -16,7 +16,11 @@ export const NotFound = () => {
|
|||
paddingTop: 100,
|
||||
}}>
|
||||
<Text style={{fontSize: 40, fontWeight: 'bold'}}>Page not found</Text>
|
||||
<Button title="Home" onPress={() => stores.nav.navigate('/')} />
|
||||
<Button
|
||||
testID="navigateHomeButton"
|
||||
title="Home"
|
||||
onPress={() => stores.nav.navigate('/')}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, {useEffect, useState, useMemo} from 'react'
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {ActivityIndicator, StyleSheet, View} from 'react-native'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
|
@ -30,7 +30,7 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
|
|||
const store = useStores()
|
||||
const onMainScroll = useOnMainScroll(store)
|
||||
const [hasSetup, setHasSetup] = useState<boolean>(false)
|
||||
const uiState = useMemo(
|
||||
const uiState = React.useMemo(
|
||||
() => new ProfileUiModel(store, {user: params.name}),
|
||||
[params.user],
|
||||
)
|
||||
|
@ -201,6 +201,7 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
|
|||
? () => (
|
||||
<>
|
||||
<FontAwesomeIcon
|
||||
testID="shouldAdminButton"
|
||||
icon="user-xmark"
|
||||
style={[s.mr5]}
|
||||
size={14}
|
||||
|
@ -242,10 +243,11 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
|
|||
const title =
|
||||
uiState.profile.displayName || uiState.profile.handle || params.name
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View testID="profileView" style={styles.container}>
|
||||
<ViewHeader title={title} />
|
||||
{uiState.profile.hasError ? (
|
||||
<ErrorScreen
|
||||
testID="profileErrorScreen"
|
||||
title="Failed to load profile"
|
||||
message={`There was an issue when attempting to load ${params.name}`}
|
||||
details={uiState.profile.error}
|
||||
|
|
|
@ -57,6 +57,7 @@ export const Search = ({navIdx, visible, params}: ScreenParams) => {
|
|||
<View style={[pal.view, pal.border, styles.inputContainer]}>
|
||||
<MagnifyingGlassIcon style={[pal.text, styles.inputIcon]} />
|
||||
<TextInput
|
||||
testID="searchTextInput"
|
||||
ref={textInput}
|
||||
placeholder="Type your query here..."
|
||||
placeholderTextColor={pal.colors.textLight}
|
||||
|
@ -68,7 +69,7 @@ export const Search = ({navIdx, visible, params}: ScreenParams) => {
|
|||
</View>
|
||||
<View style={styles.outputContainer}>
|
||||
{query ? (
|
||||
<ScrollView onScroll={Keyboard.dismiss}>
|
||||
<ScrollView testID="searchScrollView" onScroll={Keyboard.dismiss}>
|
||||
{autocompleteView.searchRes.map((item, i) => (
|
||||
<TouchableOpacity
|
||||
key={i}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue