diff --git a/__mocks__/api.ts b/__mocks__/api.ts new file mode 100644 index 0000000..7cddfdc --- /dev/null +++ b/__mocks__/api.ts @@ -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 +}; \ No newline at end of file diff --git a/app/i18n/api.ts b/app/i18n/api.ts index e25701b..c81a3a7 100644 --- a/app/i18n/api.ts +++ b/app/i18n/api.ts @@ -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 { diff --git a/app/index.tsx b/app/index.tsx index 8095b75..96f6119 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -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 ( diff --git a/components/ConversationThread.tsx b/components/ConversationThread.tsx index b2ff8e0..6972f58 100644 --- a/components/ConversationThread.tsx +++ b/components/ConversationThread.tsx @@ -67,6 +67,7 @@ const ConversationThread = ({ route } : {route?: Route<"Conversation", {conversa return cachedTranslator ? ( + Conversation Thread translator?: Translator - onLangSelected? : (lang : language_matrix_entry) => any + onLangSelected?: (lang: language_matrix_entry) => any }) { const [languages, setLanguages] = useState(); const [languagesLoaded, setLanguagesLoaded] = useState(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 ( - - - - {(languages && languagesLoaded) ? Object.entries(languages).filter((l) => (LANG_FLAGS as any)[l[0]] !== undefined).map( - ([lang, lang_entry]) => { - return ( - - ); + + + Settings + + + + + {(languages && languagesLoaded) ? Object.entries(languages).filter((l) => (LANG_FLAGS as any)[l[0]] !== undefined).map( + ([lang, lang_entry]) => { + return ( + + ); + } + ) : Waiting... } - ) : Waiting... - } - - - + + + + ) } diff --git a/components/TTNavStack.tsx b/components/TTNavStack.tsx index ceb1a9a..f9130c4 100644 --- a/components/TTNavStack.tsx +++ b/components/TTNavStack.tsx @@ -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>(); + 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 ( - + } /> diff --git a/components/__tests__/index.spec.tsx b/components/__tests__/index.spec.tsx index b551182..b576561 100644 --- a/components/__tests__/index.spec.tsx +++ b/components/__tests__/index.spec.tsx @@ -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(); - 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(); const settingsButton = await screen.findByText("Settings"); - fireEvent.press(settingsButton); + act(() => { + fireEvent.press(settingsButton) + }) expect(await screen.findByText("Settings")).toBeOnTheScreen(); }); }); \ No newline at end of file diff --git a/navigation.types.ts b/navigation.types.ts new file mode 100644 index 0000000..05bc7c2 --- /dev/null +++ b/navigation.types.ts @@ -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 }; +}; \ No newline at end of file