improve tests, especially for navigation.

This commit is contained in:
Jordan
2025-02-27 08:23:27 -08:00
parent 6f941c56d1
commit 87446784ae
24 changed files with 748 additions and 448 deletions

View File

@ -1,121 +1,142 @@
// import AsyncStorage from '@react-native-async-storage/async-storage';
import { CachedTranslator, Translator, language_matrix_entry } from "@/app/i18n/api"
import { longLang } from "@/app/i18n/lang"
import React, { useEffect, useRef, useState } from "react"
import { Button, Image, ImageBackground, Pressable, StyleSheet, TouchableOpacity, View } from "react-native"
import { Text } from 'react-native';
import {
CachedTranslator,
Translator,
language_matrix_entry,
} from "@/app/i18n/api";
import { longLang } from "@/app/i18n/lang";
import React, { useEffect, useRef, useState } from "react";
import {
Button,
Image,
ImageBackground,
Pressable,
StyleSheet,
TouchableOpacity,
View,
} from "react-native";
import { Text } from "react-native";
import CountryFlag from "react-native-country-flag";
import { chooseCountry } from '@/app/i18n/countries';
import { chooseCountry } from "@/app/i18n/countries";
type ISpeakButtonProps = {
language: language_matrix_entry,
translator?: Translator,
onLangSelected?: (lang : language_matrix_entry) => any | Promise<any>,
language: language_matrix_entry;
translator?: Translator;
onLangSelected?: (lang: language_matrix_entry) => any | Promise<any>;
};
function iSpeak(language: language_matrix_entry) {
return `I speak ${language.name}.`;
}
function iSpeak(language : language_matrix_entry) {
return `I speak ${language.name}.`
}
async function iSpeakTr(translator : CachedTranslator, targetLang : language_matrix_entry) {
const sourceStr = iSpeak(targetLang)
return await translator.translate(sourceStr, targetLang.code);
async function iSpeakTr(
translator: CachedTranslator,
targetLang: language_matrix_entry
) {
const sourceStr = iSpeak(targetLang);
return await translator.translate(sourceStr, targetLang.code);
}
const DEFAULT_FLAGS = {
"en": ["us", "gb"],
// "sq": ["al"],
"ar": ["ae"],
"es": ["es"],
"pt": ["pt"],
"ru": ["ru"],
"it": ["it"],
"ir": ["ie"],
"sk": ["sk"],
"ro": ["ro"],
"ja": ["jp"],
"ko": ["kp", "kr"],
"el": ["gr"],
"fr": ["fr"],
"de": ["de"],
"nl": ["nl"],
"cz": ["cz"],
"uk": ["ua"],
"he": ["il"],
"hi": ["in"],
"gl": ["es"],
"fa": ["ir"],
"ur": ["pk"],
"ga": ["ie"],
"eo": ["es"]
}
en: ["us", "gb"],
// "sq": ["al"],
ar: ["ae"],
es: ["es"],
pt: ["pt"],
ru: ["ru"],
it: ["it"],
ir: ["ie"],
sk: ["sk"],
ro: ["ro"],
ja: ["jp"],
ko: ["kp", "kr"],
el: ["gr"],
fr: ["fr"],
de: ["de"],
nl: ["nl"],
cz: ["cz"],
uk: ["ua"],
he: ["il"],
hi: ["in"],
gl: ["es"],
fa: ["ir"],
ur: ["pk"],
ga: ["ie"],
eo: ["es"],
};
const ISpeakButton = (props : ISpeakButtonProps) => {
const ISpeakButton = (props: ISpeakButtonProps) => {
const [title, setTitle] = useState<string | undefined>();
const [titleLoaded, setTitleLoaded] = useState<boolean>(false);
const [translator, setTranslator] = useState<Translator | undefined>(
undefined
);
const [title, setTitle] = useState<string | undefined>();
const [titleLoaded, setTitleLoaded] = useState<boolean>(false);
const translator = props.translator || new CachedTranslator("en");
useEffect(() => {
(async function () {
const tr = props.translator || (await CachedTranslator.getDefault());
if (!tr) {
console.error("Failed to construct cachedTranslator");
}
setTranslator(tr);
try {
// Replace with your actual async data fetching logic
const title2 = await iSpeakTr(tr, props.language);
setTitle(title2);
} catch (error) {
console.error("Error fetching data from %s: %s", tr.languageServer.baseUrl, error);
} finally {
setTitleLoaded(true);
}
})();
}, []);
useEffect(() => {
const fetchData = async () => {
try {
// Replace with your actual async data fetching logic
const title = await iSpeakTr(translator, props.language);
setTitle(title);
} catch (error) {
console.error('Error fetching data:', error);
} finally {
setTitleLoaded(true);
}
};
fetchData();
}, []);
const countries = DEFAULT_FLAGS[props.language.code] || chooseCountry(props.language.code);
return (
title ? (
<TouchableOpacity style={styles.button} onPress={() => props.onLangSelected && props.onLangSelected(props.language)}>
<View>
<View style={styles.flag}>
{countries &&
countries.map( c => {
return <CountryFlag isoCode={c} size={25} key={c}/> }
)
}
</View>
<View style={styles.iSpeak}>
<Text style={styles.iSpeakText}>{ title }</Text>
</View>
</View>
</TouchableOpacity>
) : (
<Text>Loading...</Text>
)
)
const countries =
// @ts-ignore
DEFAULT_FLAGS[props.language.code] || chooseCountry(props.language.code);
}
return title ? (
<TouchableOpacity
style={styles.button}
onPress={() =>
props.onLangSelected && props.onLangSelected(props.language)
}
>
<View>
<View style={styles.flag}>
{countries &&
countries.map((c) => {
return <CountryFlag isoCode={c} size={25} key={c} />;
})}
</View>
<View>
<Text style={styles.iSpeakText}>{title}</Text>
</View>
</View>
</TouchableOpacity>
) : (
<Text>Loading...</Text>
);
};
const styles = StyleSheet.create({
button: {
width: "20%",
borderRadius: 10,
borderColor: "white",
borderWidth: 1,
borderStyle: "solid",
height: 110,
alignSelf: "flex-start",
margin: 8,
},
flag: {
},
iSpeak: {
textAlign: "center",
},
iSpeakText: {
textAlign: "center"
}
})
button: {
width: "20%",
borderRadius: 10,
borderColor: "white",
borderWidth: 1,
borderStyle: "solid",
height: 110,
alignSelf: "flex-start",
margin: 8,
},
flag: {},
iSpeak: {
textAlign: "center",
},
iSpeakText: {
textAlign: "center",
},
});
export default ISpeakButton;
export default ISpeakButton;

View File

@ -1,9 +1,9 @@
import React, { Dispatch } from "react";
import { render, screen, fireEvent, act } from "@testing-library/react-native";
import SettingsComponent from "@/components/Settings";
import { Settings } from "@/app/lib/settings";
import { getDb } from "@/app/lib/db";
import { language_matrix } from "@/app/i18n/api";
import { Settings } from "@/app/lib/settings";
import { getDb, migrateDb } from "@/app/lib/db";
const RENDER_TIME = 1000;
@ -49,45 +49,10 @@ jest.mock("@/app/i18n/api", () => {
}
})
jest.mock("@/app/lib/db", () => {
return {
getDb: jest.fn(() => {
return {
runAsync: jest.fn((statement : string, value : string) => {}),
getFirstAsync: jest.fn((statement : string, value : string) => {
return []
}),
}
})
}
})
jest.mock("@/app/lib/settings", () => {
const originalModule = jest.requireActual('@/app/lib/settings');
class MockSettings {
public constructor(public db = {}) {}
public setHostLanguage = jest.fn((val : string) => {
})
public setLibretranslateBaseUrl(val : string) {
}
getHostLanguage = jest.fn(() => {
return "en"
})
getLibretranslateBaseUrl = jest.fn(() => {
return "http://localhost:5004"
});
}
return {
...originalModule,
Settings: MockSettings
}
})
describe("SettingsComponent", () => {
beforeEach(async() => {
const settings = new Settings(await getDb());
await migrateDb();
const settings = await Settings.getDefault();
await settings.setHostLanguage("en");
await settings.setLibretranslateBaseUrl("https://example.com");
})
@ -116,13 +81,12 @@ describe("SettingsComponent", () => {
test("updates host language setting when input changes", async () => {
render(<SettingsComponent />);
// Wait for the component to fetch and display the initial settings
await screen.findByText(/Host Language:/i);
await screen.findByText(/LibreTranslate Base URL:/i);
// Change the host language input value
const picker = screen.getByAccessibilityHint("language");
const picker = screen.getByAccessibilityHint("hostLanguage");
fireEvent(picker, "onvalueChange", "es");
expect(picker.props.selectedIndex).toStrictEqual(0);
});