Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat: google # persistence and auth state #117

Merged
merged 17 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NavigationContainer } from "@react-navigation/native"
import React, { useEffect } from "react"
import * as SplashScreen from "expo-splash-screen"
import RegistrationStackNavigator from "./navigation/Registration/RegistrationStackNavigator"
import MainStackNavigator from "./navigation/Main/MainStackNavigator"
import { GestureHandlerRootView } from "react-native-gesture-handler"
import styles from "./styles"
import {
Expand All @@ -12,16 +12,19 @@ import {
} from "@expo-google-fonts/jetbrains-mono"
import { SafeAreaProvider } from "react-native-safe-area-context"
import { StatusBar } from 'expo-status-bar'
import * as WebBrowser from "expo-web-browser"

SplashScreen.preventAutoHideAsync()

WebBrowser.maybeCompleteAuthSession()

export default function App() {
const [fontsLoaded] = useFonts({
JetBrainsMono_100Thin_Italic,
JetBrainsMono_400Regular,
JetBrainsMono_700Bold,
})

useEffect(() => {
async function hideSplashScreen() {
if (fontsLoaded) {
Expand All @@ -39,7 +42,7 @@ export default function App() {
<StatusBar style="dark" />
<NavigationContainer>
<SafeAreaProvider>
<RegistrationStackNavigator />
<MainStackNavigator/>
</SafeAreaProvider>
</NavigationContainer>
</GestureHandlerRootView>
Expand Down
75 changes: 75 additions & 0 deletions __test__/components/GoogleSignInButton/GoogleSignInButton.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { render, fireEvent } from '@testing-library/react-native'
import { GoogleSignInButton } from '../../../components/GoogleSignInButton/GoogleSignInButton'
import { Auth, UserCredential, signInWithCredential } from 'firebase/auth'
import * as Google from 'expo-auth-session/providers/google'

global.alert = jest.fn()

jest.mock('@react-native-async-storage/async-storage', () =>
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
)

jest.mock('firebase/auth', () => ({
getReactNativePersistence: jest.fn(() => ({} as Auth)),
initializeAuth: jest.fn(() => ({} as Auth)),
onAuthStateChanged: jest.fn(),
GoogleAuthProvider: { credential: jest.fn() },
signInWithCredential: jest.fn()
}))

jest.mock('expo-auth-session/providers/google', () => ({
useAuthRequest: jest.fn()
}))

describe('GoogleSignInButton', () => {

beforeEach(() => {
jest.clearAllMocks()
})

it('should # with Google when button is pressed and response is success', async () => {
const mockUseAuthRequest = jest.fn(() => {
return [
null,
{ type: 'success', params: { id_token: 'mockIdToken' } },
jest.fn()
]
})
const mockFunction = Google.useAuthRequest as jest.Mock
mockFunction.mockImplementation(mockUseAuthRequest)

const mockSignInWithCredential = jest.fn(() => Promise.resolve({} as UserCredential))
const mockFunction2 = signInWithCredential as jest.Mock
mockFunction2.mockImplementation(mockSignInWithCredential)

const { getByTestId } = render(<GoogleSignInButton />)
const button = getByTestId('google-sign-in-button')

fireEvent.press(button)

expect(signInWithCredential).toHaveBeenCalledTimes(1)
})

it('should throw an error upon invalid #', async () => {
const mockUseAuthRequest = jest.fn(() => {
return [
null,
{ type: 'success', params: { id_token: 'mockIdToken' } },
jest.fn()
]
})
const mockFunction = Google.useAuthRequest as jest.Mock
mockFunction.mockImplementation(mockUseAuthRequest)

const mockFunction2 = signInWithCredential as jest.Mock
mockFunction2.mockRejectedValue(new Error('Network error'))

const { getByTestId } = render(<GoogleSignInButton />)
const button = getByTestId('google-sign-in-button')

fireEvent.press(button)

expect(signInWithCredential).toHaveBeenCalledTimes(1)
expect(getByTestId('google-sign-in-button')).toBeTruthy()
})
})
65 changes: 65 additions & 0 deletions __test__/navigation/Main/MainStackNavigator.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react'
import { render } from '@testing-library/react-native'
import { NavigationContainer } from '@react-navigation/native'
import MainStackNavigator from '../../../navigation/Main/MainStackNavigator'
import { Auth, User, onAuthStateChanged } from 'firebase/auth'

jest.mock('@react-native-async-storage/async-storage', () =>
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
)

jest.mock('firebase/auth', () => ({
getReactNativePersistence: jest.fn(() => ({} as Auth)),
initializeAuth: jest.fn(() => ({} as Auth)),
onAuthStateChanged: jest.fn()
}))

jest.mock("../../../components/GoogleSignInButton/GoogleSignInButton", () => {
return {
GoogleSignInButton: () => {"Continue with google"}
}
})

describe('RegistrationStackNavigator', () => {
beforeEach(() => {
jest.clearAllMocks()
})

it('renders the stack navigator with HomeTabs when user is logged in', () => {
const mockUser: User = { uid: '123' } as User
const mockOnAuthStateChanged = jest.fn((auth, callback) => {
callback(mockUser)
return jest.fn()
})

const mockFunction = onAuthStateChanged as jest.Mock
mockFunction.mockImplementation(mockOnAuthStateChanged)

const { getByText } = render(
<NavigationContainer>
<MainStackNavigator />
</NavigationContainer>
)

expect(getByText('Home')).toBeTruthy()
expect(mockOnAuthStateChanged).toHaveBeenCalledTimes(1)
})

it('renders the stack navigator with Onboarding when user is not logged in', () => {
const mockOnAuthStateChanged = jest.fn((auth, callback) => {
callback(null)
return jest.fn()
})
const mockFunction = onAuthStateChanged as jest.Mock
mockFunction.mockImplementation(mockOnAuthStateChanged)

const { getByText } = render(
<NavigationContainer>
<MainStackNavigator />
</NavigationContainer>
)

expect(getByText('Log In')).toBeTruthy()
expect(mockOnAuthStateChanged).toHaveBeenCalledTimes(1)
})
})

This file was deleted.

26 changes: 9 additions & 17 deletions __test__/screens/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
import React from 'react'
import { render , waitFor} from '@testing-library/react-native'
import App from '../../App'
import { Auth } from 'firebase/auth'

jest.mock('@react-native-google-signin/google-signin', () => {
return {
GoogleSignin: {
configure: jest.fn(),
signIn: jest.fn(() => Promise.resolve({
idToken: 'mock-id-token',
accessToken: 'mock-access-token',
user: {
email: 'test@example.com',
id: '123',
name: 'Test User'
}
})),
signOut: jest.fn(),
}
}
})
jest.mock('@react-native-async-storage/async-storage', () =>
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
)

jest.mock("firebase/auth", () => ({
getReactNativePersistence: jest.fn(() => ({} as Auth)),
initializeAuth: jest.fn(() => ({} as Auth)),
}))

describe('App', () => {

Expand Down
40 changes: 20 additions & 20 deletions __test__/screens/Onboarding/OnboardingScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import OnboardingScreen from '../../../screens/Onboarding/OnboardingScreen'
import { NavigationContainer } from '@react-navigation/native'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import * as login from '../../../firebase/#'
import { GoogleSignin } from '@react-native-google-signin/google-signin'
import { Auth, User } from 'firebase/auth'

// Mocking modules
jest.mock('../../../firebase/#', () => ({
Expand All @@ -25,23 +25,6 @@ jest.mock('@react-navigation/native', () => {
}
})

// Setting up the Jest mock for GoogleSignin
jest.mock('@react-native-google-signin/google-signin', () => ({
GoogleSignin: {
configure: jest.fn(),
signIn: jest.fn(() => Promise.resolve({
idToken: 'mock-id-token',
accessToken: 'mock-access-token',
user: {
email: 'test@example.com',
id: '123',
name: 'Test User'
}
})),
signOut: jest.fn(),
hasPlayServices: jest.fn(() => Promise.resolve(true)), // Make sure to mock this if it's being called
}
}))

jest.mock('react-native-safe-area-context', () => {
const inset = { top: 0, right: 0, bottom: 0, left: 0 }
Expand All @@ -53,6 +36,22 @@ jest.mock('react-native-safe-area-context', () => {
}
})

jest.mock('@react-native-async-storage/async-storage', () =>
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
)

jest.mock("firebase/auth", () => ({
getReactNativePersistence: jest.fn(() => ({} as Auth)),
initializeAuth: jest.fn(() => ({} as Auth)),
onAuthStateChanged: jest.fn(() => ({uid: '123'} as User)),
}))

jest.mock("../../../components/GoogleSignInButton/GoogleSignInButton", () => {
return {
GoogleSignInButton: () => {"Continue with google"}
}
})


describe('OnboardingScreen', () => {
beforeEach(() => {
Expand All @@ -74,7 +73,6 @@ describe('OnboardingScreen', () => {
expect(getByPlaceholderText('Password')).toBeTruthy()
expect(getByText('Log In')).toBeTruthy()
expect(getByText('Forgot password?')).toBeTruthy()
expect(getByText('Continue with google')).toBeTruthy()
expect(getByText('Dont have an account?')).toBeTruthy()
})

Expand Down Expand Up @@ -107,6 +105,7 @@ describe('OnboardingScreen', () => {
expect(loginButton).toBeTruthy()
})

/*
it('on Google Login press, login succesful navigates to the home screen', async () => {
const { getByText } = render(
<SafeAreaProvider>
Expand All @@ -123,13 +122,14 @@ describe('OnboardingScreen', () => {

await waitFor(() => {
//expect navigation to be called'
expect(GoogleSignin.signIn).toHaveBeenCalled()
//expect(GoogleSignin.signIn).toHaveBeenCalled()
expect(mockNavigate).toHaveBeenCalledWith('HomeTabs')
})

expect(googleButton).toBeTruthy()

})
*/

//on log in press, login failed alert is called
it('on log in press, login failed alert is called',async () => {
Expand Down
25 changes: 25 additions & 0 deletions __test__/screens/Settings/SettingsScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { render, fireEvent } from "@testing-library/react-native"
import { SettingsScreen } from "../../../screens/Settings/SettingsScreen"
import { NavigationContainer } from "@react-navigation/native"
import { Alert } from 'react-native'
import { Auth } from 'firebase/auth'


const mockGoBack = jest.fn()
Expand All @@ -16,6 +17,19 @@ jest.mock("@react-navigation/native", () => {
}
})

jest.mock('@react-native-async-storage/async-storage', () =>
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
)

jest.mock("firebase/auth", () => ({
getReactNativePersistence: jest.fn(() => ({} as Auth)),
initializeAuth: jest.fn(() => ({ signOut: jest.fn() })),
}))

jest.mock("../../../firebase/firebaseConfig", () => ({
auth: { signOut: jest.fn(() => Promise.resolve()) }
}))

describe("SettingsScreen", () => {
test("renders correctly", () => {
const { getByTestId, getByText } = render(
Expand Down Expand Up @@ -68,4 +82,15 @@ describe("SettingsScreen", () => {

expect(alertSpy).toHaveBeenCalledWith("Coming soon")
})

test("calls the correct action when the logout menu item is pressed", () => {
const { getByText } = render(
<NavigationContainer>
<SettingsScreen />
</NavigationContainer>
)

const menuItem = getByText("LOG OUT")
fireEvent.press(menuItem)
})
})
Loading
Loading