import { act, fireEvent, render, screen, waitFor } from "@testing-library/react"
import { beforeEach, describe, expect, it, vi } from "vitest"
import { DevelopmentTab } from "../../../components/tabs/development-tab"
import { useApiKeys } from "../../../hooks/use-api-keys"
vi.mock("../../../hooks/use-api-keys")
vi.mock("react-i18next", () => ({
useTranslation: () => ({
t: (key: string, defaultValue?: string) => defaultValue || key,
}),
}))
vi.mock("lucide-react", () => ({
ExternalLink: ({ className }: { className?: string }) => (
ExternalLink
),
Eye: ({ className }: { className?: string }) => (
Eye
),
EyeOff: ({ className }: { className?: string }) => (
EyeOff
),
Loader2: ({ className }: { className?: string }) => (
Loader2
),
X: ({ className }: { className?: string }) => (
X
),
}))
vi.spyOn(console, "log").mockImplementation(() => {})
vi.spyOn(console, "error").mockImplementation(() => {})
describe("DevelopmentTab", () => {
const mockSaveSimpleApiKey = vi.fn()
const mockGetApiKeyInfo = vi.fn()
beforeEach(() => {
vi.clearAllMocks()
// Mock process.env.NODE_ENV
vi.stubEnv("NODE_ENV", "development")
vi.mocked(useApiKeys).mockImplementation(() => ({
getApiKeyStatus: vi.fn().mockReturnValue("not_set"),
getApiKeyInfo: mockGetApiKeyInfo,
testApiKey: vi.fn(),
saveSimpleApiKey: mockSaveSimpleApiKey,
deleteApiKey: vi.fn(),
loadApiKeysInfo: vi.fn(),
saveOAuthCredentials: vi.fn(),
generateOAuthUrl: vi.fn(),
exchangeOAuthCode: vi.fn(),
refreshOAuthToken: vi.fn(),
getOAuthUserInfo: vi.fn(),
parseOAuthCallbackUrl: vi.fn(),
importFromEnv: vi.fn(),
exportToEnvFormat: vi.fn(),
apiKeysInfo: {},
loadingStatuses: {},
}))
})
afterEach(() => {
vi.unstubAllEnvs()
})
it("should render correctly in development mode", () => {
render()
expect(screen.getByText("Разработка")).toBeInTheDocument()
expect(
screen.getByText("Настройки для инструментов разработки и аналитики. Доступно только в режиме разработки."),
).toBeInTheDocument()
expect(screen.getByText("Codecov Token")).toBeInTheDocument()
expect(
screen.getByText("Токен для отправки отчетов покрытия тестами в Codecov. Используется в CI/CD pipeline."),
).toBeInTheDocument()
expect(screen.getByText("Tauri Analytics Key")).toBeInTheDocument()
expect(
screen.getByText("Ключ для аналитики Tauri приложения. Используется для сбора метрик производительности."),
).toBeInTheDocument()
expect(screen.getByText("Режим разработки")).toBeInTheDocument()
expect(
screen.getByText(
"Эта вкладка видна только в режиме разработки. В production сборке настройки разработки недоступны.",
),
).toBeInTheDocument()
// The inputs are password type initially
const apiKeyInputs = screen.getAllByPlaceholderText(/your_/)
expect(apiKeyInputs).toHaveLength(2)
const linkButtons = screen.getAllByRole("button")
expect(linkButtons).toHaveLength(2)
})
it("should not render in production mode", () => {
vi.stubEnv("NODE_ENV", "production")
const { container } = render()
expect(container.firstChild).toBeNull()
})
it("should not render in test mode when set to production", () => {
vi.stubEnv("NODE_ENV", "production")
const { container } = render()
expect(container.firstChild).toBeNull()
})
it("should load existing API keys on mount", () => {
mockGetApiKeyInfo.mockReturnValueOnce({ has_value: true })
mockGetApiKeyInfo.mockReturnValueOnce({ has_value: true })
render()
expect(mockGetApiKeyInfo).toHaveBeenCalledWith("codecov")
expect(mockGetApiKeyInfo).toHaveBeenCalledWith("tauri_analytics")
// The inputs are password type, so we need to find them by placeholder
const codecovInput = screen.getByPlaceholderText("your_codecov_token_here")
const tauriInput = screen.getByPlaceholderText("your_tauri_analytics_key")
expect(codecovInput).toHaveValue("••••••••••••••••••••••••••••••••••••••••••••••••••••")
expect(tauriInput).toHaveValue("••••••••••••••••••••••••••••••••••••••••••••••••••••")
})
it("should handle Codecov token input changes", async () => {
render()
const codecovInput = screen.getByPlaceholderText("your_codecov_token_here")
act(() => {
fireEvent.change(codecovInput, { target: { value: "codecov-test-token" } })
})
expect(codecovInput).toHaveValue("codecov-test-token")
await waitFor(() => {
expect(mockSaveSimpleApiKey).toHaveBeenCalledWith("codecov", "codecov-test-token")
})
})
it("should handle Tauri Analytics key input changes", async () => {
render()
const tauriInput = screen.getByPlaceholderText("your_tauri_analytics_key")
act(() => {
fireEvent.change(tauriInput, { target: { value: "tauri-analytics-key" } })
})
expect(tauriInput).toHaveValue("tauri-analytics-key")
await waitFor(() => {
expect(mockSaveSimpleApiKey).toHaveBeenCalledWith("tauri_analytics", "tauri-analytics-key")
})
})
it("should not save keys with masked characters", () => {
render()
const codecovInput = screen.getByPlaceholderText("your_codecov_token_here")
act(() => {
fireEvent.change(codecovInput, { target: { value: "••••••••••••" } })
})
expect(mockSaveSimpleApiKey).not.toHaveBeenCalled()
})
it("should not save empty keys", () => {
render()
const codecovInput = screen.getByPlaceholderText("your_codecov_token_here")
act(() => {
fireEvent.change(codecovInput, { target: { value: "" } })
})
expect(mockSaveSimpleApiKey).not.toHaveBeenCalled()
})
it("should handle link button clicks to open external URLs", () => {
const mockWindowOpen = vi.spyOn(window, "open").mockImplementation(() => null)
render()
const linkButtons = screen.getAllByRole("button")
act(() => {
fireEvent.click(linkButtons[0])
})
expect(mockWindowOpen).toHaveBeenCalledWith("https://app.codecov.io/settings", "_blank")
act(() => {
fireEvent.click(linkButtons[1])
})
expect(mockWindowOpen).toHaveBeenCalledWith("https://tauri.app/v1/guides/features/analytics/", "_blank")
mockWindowOpen.mockRestore()
})
it("should render API key inputs without test buttons", () => {
render()
const testButtons = screen.queryAllByText("Тест")
expect(testButtons).toHaveLength(0)
})
it("should display all separator elements", () => {
render()
const separators = document.querySelectorAll('[role="none"]')
expect(separators).toHaveLength(2)
})
it("should maintain separate state for each API key input", async () => {
render()
const codecovInput = screen.getByPlaceholderText("your_codecov_token_here")
const tauriInput = screen.getByPlaceholderText("your_tauri_analytics_key")
act(() => {
fireEvent.change(codecovInput, { target: { value: "codecov-key" } })
})
act(() => {
fireEvent.change(tauriInput, { target: { value: "tauri-key" } })
})
expect(codecovInput).toHaveValue("codecov-key")
expect(tauriInput).toHaveValue("tauri-key")
await waitFor(() => {
expect(mockSaveSimpleApiKey).toHaveBeenCalledWith("codecov", "codecov-key")
expect(mockSaveSimpleApiKey).toHaveBeenCalledWith("tauri_analytics", "tauri-key")
})
})
it("should load different values for different services", () => {
mockGetApiKeyInfo.mockReturnValueOnce({ has_value: true }).mockReturnValueOnce({ has_value: false })
render()
// The inputs are password type, so we need to find them by placeholder
const codecovInput = screen.getByPlaceholderText("your_codecov_token_here")
const tauriInput = screen.getByPlaceholderText("your_tauri_analytics_key")
expect(codecovInput).toHaveValue("••••••••••••••••••••••••••••••••••••••••••••••••••••")
expect(tauriInput).toHaveValue("")
})
it("should handle multiple quick changes correctly", async () => {
render()
const codecovInput = screen.getByPlaceholderText("your_codecov_token_here")
act(() => {
fireEvent.change(codecovInput, { target: { value: "test-1" } })
})
act(() => {
fireEvent.change(codecovInput, { target: { value: "test-12" } })
})
act(() => {
fireEvent.change(codecovInput, { target: { value: "test-123" } })
})
await waitFor(() => {
expect(mockSaveSimpleApiKey).toHaveBeenLastCalledWith("codecov", "test-123")
})
})
it("should render development warning box with correct styling", () => {
render()
const warningBox = screen.getByText("Режим разработки").parentElement
expect(warningBox).toHaveClass("bg-amber-50")
expect(warningBox).toHaveClass("border-amber-200")
})
})