improve tests, especially for navigation.
This commit is contained in:
@ -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;
|
||||
|
@ -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);
|
||||
});
|
||||
|
Reference in New Issue
Block a user