import React, { Dispatch } from "react"; import { render, screen, fireEvent, act } from "@testing-library/react-native"; import SettingsComponent from "@/components/Settings"; import { language_matrix } from "@/app/i18n/api"; import { Settings } from "@/app/lib/settings"; import { getDb } from "@/app/lib/db"; import { Knex } from "knex"; import { WhisperFile } from "@/app/lib/whisper"; const RENDER_TIME = 1000; jest.mock("@/app/lib/whisper", () => { const originalModule = jest.requireActual("@/app/lib/whisper"); return { ...originalModule, WhisperFile: jest.fn().mockImplementation((tag, targetFileName, label, size) => ({ tag, targetFileName, label, size, doesTargetExist: jest.fn(), getDownloadStatus: jest.fn(), // Mock other methods as needed isDownloadComplete: jest.fn(() => false), // Initially assume download is not complete createDownloadResumable: jest.fn().mockResolvedValue({ startAsync: jest.fn().mockResolvedValue({}), }), })), }; }); jest.mock("expo-file-system", () => { const originalModule = jest.requireActual("expo-file-system"); return { ...originalModule, File: jest.fn().mockImplementation(() => ({ bytes: jest.fn(), exists: jest.fn(), })), }; }); jest.mock("@/app/i18n/api", () => { const LanguageServer = jest.fn(); const Translator = jest.fn(); // Mock the fetchLanguages method to return a predefined language matrix LanguageServer.prototype.fetchLanguages = jest.fn(() => ({ en: { code: "en", name: "English", targets: ["fr", "es"], }, fr: { code: "fr", name: "French", targets: ["en", "es"], }, es: { code: "es", name: "Spanish", targets: ["en", "fr"], }, } as language_matrix)); // Mock the translate method Translator.prototype.translate = jest.fn((text: string, target: string) => { return "Hola, como estas?"; }); return { LanguageServer, Translator, }; }); describe("SettingsComponent", () => { let db: Knex; let settings: Settings; beforeEach(async () => { db = await getDb("development"); settings = new Settings(db); jest.spyOn(Settings, 'getDefault').mockResolvedValue(settings); await settings.setHostLanguage("en"); await settings.setLibretranslateBaseUrl("https://example.com"); }); afterEach(async () => { jest.restoreAllMocks(); await db.migrate.down(); await db.destroy(); }); beforeAll(async () => { jest.useFakeTimers(); }); afterAll(() => { jest.useRealTimers(); }); test("renders correctly with initial settings", async () => { render(); screen.debug(); // Wait for the component to fetch and display the initial settings await screen.findByText(/Host Language:/i); await screen.findByText(/LibreTranslate Base URL:/i); // expect(screen.getByDisplayValue("English")).toBeTruthy(); expect( screen.getByAccessibilityHint("libretranslate base url") ).toBeTruthy(); }); test("updates host language setting when input changes", async () => { render(); // 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("host language"); fireEvent(picker, "onvalueChange", "es"); expect(picker.props.selectedIndex).toStrictEqual(0); }); test("updates LibreTranslate base URL setting when input changes", async () => { render(); jest.advanceTimersByTime(RENDER_TIME); screen.debug(); // 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 LibreTranslate base URL input value fireEvent.changeText( screen.getByAccessibilityHint("libretranslate base url"), "http://new-example.com" ); jest.advanceTimersByTime(RENDER_TIME); expect( screen.getByAccessibilityHint("libretranslate base url") ).toBeTruthy(); }); describe("Download Whisper Model", () => { it("should trigger download when model is not present", async () => { const whisperFile = new WhisperFile("small"); (whisperFile.doesTargetExist as jest.Mock).mockResolvedValue(false); render(); await screen.findByText(/\s*Download Small\s*/i); // Assuming there's a button or trigger to start download act(() => { fireEvent.press(screen.getByText(/\s*Download Small\s*/i)); }) expect(whisperFile.createDownloadResumable).toHaveBeenCalled(); }); it("should show progress when download is in progress", async () => { const whisperFile = new WhisperFile("small"); (whisperFile.doesTargetExist as jest.Mock).mockResolvedValue(false); (whisperFile.getDownloadStatus as jest.Mock).mockResolvedValue({ doesTargetExist: false, isDownloadComplete: false, hasDownloadStarted: true, progress: { current: 1024, total: 2048, remaining: 1024, percentRemaining: 50, }, }); render(); await screen.findByText(/Host Language:/i); fireEvent.press(screen.getByText(/Download Model/i)); expect(await screen.findByText("50%")).toBeTruthy(); }); it("should indicate download is complete", async () => { const whisperFile = new WhisperFile("small"); (whisperFile.doesTargetExist as jest.Mock).mockResolvedValue(false); (whisperFile.getDownloadStatus as jest.Mock) .mockResolvedValueOnce({ doesTargetExist: false, isDownloadComplete: false, hasDownloadStarted: true, progress: { current: 1024, total: 2048, remaining: 1024, percentRemaining: 50, }, }) .mockResolvedValueOnce({ doesTargetExist: true, isDownloadComplete: true, hasDownloadStarted: false, progress: undefined, }); render(); await screen.findByText(/Host Language:/i); fireEvent.press(screen.getByText(/Download Model/i)); expect(await screen.findByText("Download Complete")).toBeTruthy(); }); }); });