Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
14 changes: 14 additions & 0 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { AuthProvider } from './src/context/AuthContext';
import RootNavigator from './src/navigation/RootNavigator';

export default function App() {
return (
<AuthProvider>
<NavigationContainer>
<RootNavigator />
</NavigationContainer>
</AuthProvider>
);
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ This command will move the starter code to the **app-example** directory and cre

## Start Contributing

Please checkout the Contrbiuting Section for more information
Please checkout the Contributing Section for more information
108 changes: 108 additions & 0 deletions __tests__/AuthContext.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import type {} from '@jest/globals';
import React from 'react';
import { render, act, fireEvent, waitFor } from '@testing-library/react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { AuthProvider, useAuth } from '../src/context/AuthContext';
import { Pressable, Text } from 'react-native';

jest.mock('@react-native-async-storage/async-storage', () => ({
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
}));

const TestComponent = () => {
const { state, login, logout } = useAuth();
return (
<>
<Text>{state.isAuthenticated ? 'Authenticated' : 'Not Authenticated'}</Text>
<Text>{state.isLoading ? 'Loading' : 'Loaded'}</Text>
<Text>{state.accessToken}</Text>
<Text>{state.refreshToken}</Text>
<Pressable accessibilityLabel="login" onPress={() => login('user', 'pass')}>
<Text>Login</Text>
</Pressable>
<Pressable accessibilityLabel="logout" onPress={logout}>
<Text>Logout</Text>
</Pressable>
</>
);
};

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

it('should initialize with loading state', async () => {
(AsyncStorage.getItem as jest.Mock).mockResolvedValue(null);

const { getByText } = render(
<AuthProvider>
<TestComponent />
</AuthProvider>
);

expect(getByText('Loading')).toBeTruthy();
});

it('should restore tokens and set authenticated', async () => {
(AsyncStorage.getItem as jest.Mock)
.mockImplementationOnce(() => Promise.resolve('abc123')) // accessToken
.mockImplementationOnce(() => Promise.resolve('xyz789')); // refreshToken

const { getByText } = render(
<AuthProvider>
<TestComponent />
</AuthProvider>
);

await waitFor(() => expect(getByText('Authenticated')).toBeTruthy());
expect(getByText('Loaded')).toBeTruthy();
expect(getByText('abc123')).toBeTruthy();
expect(getByText('xyz789')).toBeTruthy();
});

it('should login and set tokens', async () => {
(AsyncStorage.setItem as jest.Mock).mockResolvedValue(undefined);

const { getByText, getByLabelText } = render(
<AuthProvider>
<TestComponent />
</AuthProvider>
);

await act(async () => {
fireEvent.press(getByLabelText('login'));
});

await waitFor(() => expect(getByText('Authenticated')).toBeTruthy());
expect(AsyncStorage.setItem).toHaveBeenCalledWith('accessToken', 'abc123');
expect(AsyncStorage.setItem).toHaveBeenCalledWith('refreshToken', 'xyz789');
});

it('should logout and clear tokens', async () => {
(AsyncStorage.setItem as jest.Mock).mockResolvedValue(undefined);
(AsyncStorage.removeItem as jest.Mock).mockResolvedValue(undefined);

const { getByText, getByLabelText } = render(
<AuthProvider>
<TestComponent />
</AuthProvider>
);

// Login first
await act(async () => {
fireEvent.press(getByLabelText('login'));
});

// Then logout
await act(async () => {
fireEvent.press(getByLabelText('logout'));
});

await waitFor(() => expect(getByText('Not Authenticated')).toBeTruthy());
expect(AsyncStorage.removeItem).toHaveBeenCalledWith('accessToken');
expect(AsyncStorage.removeItem).toHaveBeenCalledWith('refreshToken');
});
});
1 change: 0 additions & 1 deletion app.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
"favicon": "./assets/images/favicon.png"
},
"plugins": [
"expo-router",
[
"expo-splash-screen",
{
Expand Down
45 changes: 0 additions & 45 deletions app/(tabs)/_layout.tsx

This file was deleted.

39 changes: 0 additions & 39 deletions app/(tabs)/explore.tsx

This file was deleted.

44 changes: 0 additions & 44 deletions app/(tabs)/index.tsx

This file was deleted.

32 changes: 0 additions & 32 deletions app/+not-found.tsx

This file was deleted.

29 changes: 0 additions & 29 deletions app/_layout.tsx

This file was deleted.

45 changes: 0 additions & 45 deletions components/Collapsible.tsx

This file was deleted.

Loading