work on navigation component workflow.
This commit is contained in:
parent
6673663883
commit
eb7599bfe8
38
__mocks__/api.ts
Normal file
38
__mocks__/api.ts
Normal file
@ -0,0 +1,38 @@
|
||||
// __mocks__/api.ts
|
||||
|
||||
import { language_matrix, language_matrix_entry } from "@/app/i18n/api";
|
||||
|
||||
// Import the actual API module to extend its functionality
|
||||
const origApi = jest.requireActual('@/app/i18n/api.ts');
|
||||
|
||||
class LanguageServer {
|
||||
constructor(...args: any[]) { }
|
||||
fetchLanguages(): language_matrix {
|
||||
return {
|
||||
"en" : { code: "en", name: "English", targets: ['fr', 'es'] },
|
||||
"fr" : { code: "fr", name: "French", targets: ["en", "es"] },
|
||||
"es": { code: "es", name: "Spanish", targets: ['fr', 'en'] },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Translator {
|
||||
constructor(...args : any []) {}
|
||||
translate(message : string, target : string) {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
class CachedTranslator extends Translator{
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
...origApi,
|
||||
LanguageServer,
|
||||
Translator,
|
||||
CachedTranslator,
|
||||
// Mock the specific functions you want to override
|
||||
fetchData: jest.fn(() => Promise.resolve({ data: 'mocked data' })),
|
||||
// Add more mock implementations as needed
|
||||
};
|
@ -83,6 +83,12 @@ export class Translator {
|
||||
console.log(data)
|
||||
return data.translatedText
|
||||
}
|
||||
|
||||
static async getDefault(defaultTarget: string | undefined = undefined) {
|
||||
const settings = await Settings.getDefault();
|
||||
const source = await settings.getHostLanguage();
|
||||
return new Translator(source, defaultTarget, await LanguageServer.getDefault())
|
||||
}
|
||||
}
|
||||
|
||||
export class CachedTranslator extends Translator {
|
||||
|
@ -4,6 +4,7 @@ import { Image, Text, View, StyleSheet, Button, Pressable } from "react-native";
|
||||
import { LanguageServer, Translator, language_matrix_entry } from "./i18n/api";
|
||||
import { Conversation } from "./lib/conversation";
|
||||
import { LanguageSelection } from "@/components/LanguageSelection";
|
||||
import { Link } from 'expo-router';
|
||||
|
||||
function LogoTitle() {
|
||||
return (
|
||||
|
@ -67,6 +67,7 @@ const ConversationThread = ({ route } : {route?: Route<"Conversation", {conversa
|
||||
|
||||
return cachedTranslator ? (
|
||||
<View style={{ flex: 1, flexDirection: "column" }}>
|
||||
<Text>Conversation Thread</Text>
|
||||
<ScrollView
|
||||
style={{
|
||||
borderColor: "black",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { CachedTranslator, Translator, language_matrix, language_matrix_entry } from "@/app/i18n/api";
|
||||
import { CachedTranslator, LanguageServer, Translator, language_matrix, language_matrix_entry } from "@/app/i18n/api";
|
||||
import { LIBRETRANSLATE_BASE_URL } from "@/constants/api";
|
||||
import { useEffect, useState } from "react";
|
||||
import ISpeakButton from "./ui/ISpeakButton";
|
||||
@ -7,17 +7,19 @@ import { ScrollView, StyleSheet, Text, View } from "react-native";
|
||||
import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context";
|
||||
import { Conversation, Speaker } from "@/app/lib/conversation";
|
||||
import { NavigationProp, ParamListBase } from "@react-navigation/native";
|
||||
import { Link } from "expo-router";
|
||||
|
||||
|
||||
export function LanguageSelection(props: {
|
||||
navigation?: NavigationProp<ParamListBase>
|
||||
translator?: Translator
|
||||
onLangSelected? : (lang : language_matrix_entry) => any
|
||||
onLangSelected?: (lang: language_matrix_entry) => any
|
||||
}) {
|
||||
const [languages, setLanguages] = useState<language_matrix | undefined>();
|
||||
const [languagesLoaded, setLanguagesLoaded] = useState<boolean>(false);
|
||||
|
||||
const translator = props.translator || new CachedTranslator("en")
|
||||
const languageServer = new LanguageServer(LIBRETRANSLATE_BASE_URL);
|
||||
const translator = props.translator || new CachedTranslator("en", undefined, languageServer);
|
||||
|
||||
function onLangSelected(language: language_matrix_entry) {
|
||||
props.onLangSelected && props.onLangSelected(language)
|
||||
@ -28,7 +30,7 @@ export function LanguageSelection(props: {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
// Replace with your actual async data fetching logic
|
||||
const languages = await translator.fetchLanguages();
|
||||
const languages = await languageServer.fetchLanguages();
|
||||
setLanguages(languages);
|
||||
setLanguagesLoaded(true);
|
||||
} catch (error) {
|
||||
@ -41,20 +43,25 @@ export function LanguageSelection(props: {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ScrollView >
|
||||
<SafeAreaProvider >
|
||||
<SafeAreaView>
|
||||
{(languages && languagesLoaded) ? Object.entries(languages).filter((l) => (LANG_FLAGS as any)[l[0]] !== undefined).map(
|
||||
([lang, lang_entry]) => {
|
||||
return (
|
||||
<ISpeakButton language={lang_entry} key={lang_entry.code} onLangSelected={onLangSelected} />
|
||||
);
|
||||
<View>
|
||||
<Link href={"/settings"}>
|
||||
<Text>Settings</Text>
|
||||
</Link>
|
||||
<ScrollView >
|
||||
<SafeAreaProvider >
|
||||
<SafeAreaView>
|
||||
{(languages && languagesLoaded) ? Object.entries(languages).filter((l) => (LANG_FLAGS as any)[l[0]] !== undefined).map(
|
||||
([lang, lang_entry]) => {
|
||||
return (
|
||||
<ISpeakButton language={lang_entry} key={lang_entry.code} onLangSelected={onLangSelected} />
|
||||
);
|
||||
}
|
||||
) : <Text>Waiting...</Text>
|
||||
}
|
||||
) : <Text>Waiting...</Text>
|
||||
}
|
||||
</SafeAreaView>
|
||||
</SafeAreaProvider>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
</SafeAreaProvider>
|
||||
</ScrollView>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2,16 +2,38 @@ import * as React from 'react';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import SettingsComponent from '@/components/Settings';
|
||||
import { LanguageSelection } from '@/components/LanguageSelection';
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
import { createNativeStackNavigator, NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import {
|
||||
useNavigation,
|
||||
} from '@react-navigation/native'
|
||||
import ConversationThread from '@/components/ConversationThread';
|
||||
import { language_matrix_entry, Translator } from '@/app/i18n/api';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { Conversation } from '@/app/lib/conversation';
|
||||
import { Settings } from '@/app/lib/settings';
|
||||
import { RootStackParamList } from '@/navigation.types';
|
||||
|
||||
const Stack = createNativeStackNavigator();
|
||||
|
||||
export default function TTNavStack() {
|
||||
|
||||
|
||||
async function onLangSelected(lang: language_matrix_entry) {
|
||||
const nav = useNavigation<NativeStackNavigationProp<RootStackParamList, 'ConversationThread'>>();
|
||||
const settings = await Settings.getDefault();
|
||||
const hostLanguage = await settings.getHostLanguage();
|
||||
const conversation = new Conversation(
|
||||
(await Translator.getDefault(lang.code)),
|
||||
{ id: "host", language: hostLanguage },
|
||||
{ "id": "guest", language: lang.code, }
|
||||
)
|
||||
nav.navigate("Conversation", { conversation, })
|
||||
}
|
||||
|
||||
return (
|
||||
<NavigationContainer>
|
||||
<Stack.Navigator initialRouteName='LanguageSelection'>
|
||||
<Stack.Screen name="LanguageSelection" component={LanguageSelection} />
|
||||
<Stack.Screen name="LanguageSelection" component={({ ...props }) => <LanguageSelection {...props} onLangSelected={onLangSelected} />} />
|
||||
<Stack.Screen name="ConversationThread" component={ConversationThread} />
|
||||
<Stack.Screen name="Settings" component={SettingsComponent} />
|
||||
</Stack.Navigator>
|
||||
|
@ -1,17 +1,11 @@
|
||||
import {dirname, resolve} from 'path'
|
||||
import React from 'react';
|
||||
import { fireEvent, render, screen } from '@testing-library/react-native';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { act, fireEvent, render, screen } from '@testing-library/react-native';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import TTNavStack from '../TTNavStack';
|
||||
|
||||
// Mock the expo-file-system/next module
|
||||
jest.mock('expo-file-system/next', () => ({
|
||||
File: jest.fn(),
|
||||
Paths: {
|
||||
join: jest.fn(),
|
||||
},
|
||||
}));
|
||||
jest.mock("@/app/i18n/api", () => require("../../__mocks__/api.ts"));
|
||||
|
||||
import TTNavStack from '../TTNavStack';
|
||||
|
||||
const Stack = createStackNavigator();
|
||||
|
||||
@ -23,15 +17,19 @@ describe('Navigation', () => {
|
||||
|
||||
it('Navigates to ConversationThread on language selection', async () => {
|
||||
render(<TTNavStack />);
|
||||
const languageSelectionText = await screen.findByText("Language Selection");
|
||||
fireEvent.press(languageSelectionText);
|
||||
const languageSelectionText = await screen.findByText(/I Speak French\./i);
|
||||
act(() => {
|
||||
fireEvent.press(languageSelectionText);
|
||||
})
|
||||
expect(await screen.findByText("Conversation Thread")).toBeOnTheScreen();
|
||||
});
|
||||
|
||||
it('Navigates to Settings on settings selection', async () => {
|
||||
render(<TTNavStack />);
|
||||
const settingsButton = await screen.findByText("Settings");
|
||||
fireEvent.press(settingsButton);
|
||||
act(() => {
|
||||
fireEvent.press(settingsButton)
|
||||
})
|
||||
expect(await screen.findByText("Settings")).toBeOnTheScreen();
|
||||
});
|
||||
});
|
11
navigation.types.ts
Normal file
11
navigation.types.ts
Normal file
@ -0,0 +1,11 @@
|
||||
// navigation.types.ts
|
||||
|
||||
import { ParamListBase } from '@react-navigation/native';
|
||||
import { Conversation } from '@/app/lib/conversation';
|
||||
|
||||
export type RootStackParamList = {
|
||||
LanguageSelection: undefined;
|
||||
ConversationThread: undefined;
|
||||
Settings: undefined;
|
||||
Conversation: { conversation: Conversation };
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user