mirror of
https://github.com/mengxi-ream/read-frog.git
synced 2026-04-30 01:56:46 +00:00
feat: sync split translator shortcut display
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
2e1d05ca85
commit
fa00a20b2c
2 changed files with 80 additions and 12 deletions
|
|
@ -2,7 +2,8 @@
|
|||
import { fireEvent, render, screen, waitFor } from "@testing-library/react"
|
||||
import { afterEach, describe, expect, it, vi } from "vitest"
|
||||
|
||||
const i18nTMock = vi.hoisted(() => vi.fn((key: string, values?: unknown[]) => values ? `${key}:${values.join(",")}` : key))
|
||||
const i18nTMock = vi.hoisted(() => vi.fn((key: string) => key))
|
||||
const getExtensionCommandShortcutMock = vi.fn()
|
||||
const openExtensionShortcutSettingsMock = vi.fn()
|
||||
const toastErrorMock = vi.fn()
|
||||
|
||||
|
|
@ -26,6 +27,14 @@ vi.mock("@/components/ui/base-ui/button", () => ({
|
|||
),
|
||||
}))
|
||||
|
||||
vi.mock("@/components/ui/base-ui/input", () => ({
|
||||
Input: (props: React.ComponentProps<"input">) => <input {...props} />,
|
||||
}))
|
||||
|
||||
vi.mock("@/utils/extension-command-shortcut", () => ({
|
||||
getExtensionCommandShortcut: (...args: unknown[]) => getExtensionCommandShortcutMock(...args),
|
||||
}))
|
||||
|
||||
vi.mock("@/utils/navigation", () => ({
|
||||
openExtensionShortcutSettings: (...args: unknown[]) => openExtensionShortcutSettingsMock(...args),
|
||||
}))
|
||||
|
|
@ -46,16 +55,35 @@ async function renderSplitTranslatorShortcut() {
|
|||
}
|
||||
|
||||
describe("splitTranslatorShortcut", () => {
|
||||
it("renders the split translator shortcut settings card", async () => {
|
||||
it("renders the current split translator shortcut when one is configured", async () => {
|
||||
getExtensionCommandShortcutMock.mockResolvedValue("Alt+S")
|
||||
|
||||
await renderSplitTranslatorShortcut()
|
||||
|
||||
expect(screen.getByText("options.translation.splitTranslatorShortcut.title")).toBeInTheDocument()
|
||||
expect(screen.getByText("options.translation.splitTranslatorShortcut.description:Alt+S")).toBeInTheDocument()
|
||||
expect(screen.getByText("options.translation.splitTranslatorShortcut.description")).toBeInTheDocument()
|
||||
expect(await screen.findByDisplayValue("Alt+S")).toBeInTheDocument()
|
||||
expect(screen.getByRole("button", { name: "options.translation.splitTranslatorShortcut.openSettings" })).toBeInTheDocument()
|
||||
expect(i18nTMock).toHaveBeenCalledWith("options.translation.splitTranslatorShortcut.description", ["Alt+S"])
|
||||
})
|
||||
|
||||
it("renders an unset label when the browser command has no shortcut", async () => {
|
||||
getExtensionCommandShortcutMock.mockResolvedValue("")
|
||||
|
||||
await renderSplitTranslatorShortcut()
|
||||
|
||||
expect(await screen.findByDisplayValue("options.translation.splitTranslatorShortcut.unset")).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("falls back to the unset label when reading the browser command fails", async () => {
|
||||
getExtensionCommandShortcutMock.mockRejectedValue(new Error("blocked"))
|
||||
|
||||
await renderSplitTranslatorShortcut()
|
||||
|
||||
expect(await screen.findByDisplayValue("options.translation.splitTranslatorShortcut.unset")).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("opens browser shortcut settings when clicked", async () => {
|
||||
getExtensionCommandShortcutMock.mockResolvedValue("Alt+S")
|
||||
openExtensionShortcutSettingsMock.mockResolvedValue(undefined)
|
||||
|
||||
await renderSplitTranslatorShortcut()
|
||||
|
|
@ -68,6 +96,7 @@ describe("splitTranslatorShortcut", () => {
|
|||
})
|
||||
|
||||
it("shows an error toast when browser shortcut settings cannot be opened", async () => {
|
||||
getExtensionCommandShortcutMock.mockResolvedValue("Alt+S")
|
||||
openExtensionShortcutSettingsMock.mockRejectedValue(new Error("blocked"))
|
||||
|
||||
await renderSplitTranslatorShortcut()
|
||||
|
|
|
|||
|
|
@ -1,27 +1,66 @@
|
|||
import { i18n } from "#imports"
|
||||
import { useEffect, useState } from "react"
|
||||
import { toast } from "sonner"
|
||||
import { Button } from "@/components/ui/base-ui/button"
|
||||
import { Input } from "@/components/ui/base-ui/input"
|
||||
import { SPLIT_TRANSLATOR_COMMAND } from "@/entrypoints/background/split-translator-command"
|
||||
import { getExtensionCommandShortcut } from "@/utils/extension-command-shortcut"
|
||||
import { openExtensionShortcutSettings } from "@/utils/navigation"
|
||||
import { formatPageTranslationShortcut } from "@/utils/page-translation-shortcut"
|
||||
import { ConfigCard } from "../../components/config-card"
|
||||
|
||||
const DEFAULT_SPLIT_TRANSLATOR_SHORTCUT = "Alt+S"
|
||||
|
||||
export function SplitTranslatorShortcut() {
|
||||
const t = i18n.t as (key: string, values?: string[]) => string
|
||||
const [shortcut, setShortcut] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
|
||||
void getExtensionCommandShortcut(SPLIT_TRANSLATOR_COMMAND)
|
||||
.then((nextShortcut) => {
|
||||
if (!cancelled) {
|
||||
setShortcut(nextShortcut)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) {
|
||||
setShortcut("")
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleOpenShortcutSettings = () => {
|
||||
void openExtensionShortcutSettings().catch(() => {
|
||||
toast.error(i18n.t("options.translation.splitTranslatorShortcut.openFailed"))
|
||||
toast.error(t("options.translation.splitTranslatorShortcut.openFailed"))
|
||||
})
|
||||
}
|
||||
|
||||
const displayShortcut = shortcut === null
|
||||
? ""
|
||||
: shortcut
|
||||
? formatPageTranslationShortcut(shortcut)
|
||||
: t("options.translation.splitTranslatorShortcut.unset")
|
||||
|
||||
return (
|
||||
<ConfigCard
|
||||
id="split-translator-shortcut"
|
||||
title={i18n.t("options.translation.splitTranslatorShortcut.title")}
|
||||
description={i18n.t("options.translation.splitTranslatorShortcut.description", [DEFAULT_SPLIT_TRANSLATOR_SHORTCUT])}
|
||||
title={t("options.translation.splitTranslatorShortcut.title")}
|
||||
description={t("options.translation.splitTranslatorShortcut.description")}
|
||||
>
|
||||
<Button type="button" variant="outline" onClick={handleOpenShortcutSettings}>
|
||||
{i18n.t("options.translation.splitTranslatorShortcut.openSettings")}
|
||||
</Button>
|
||||
<div className="flex max-w-sm flex-col gap-3">
|
||||
<Input
|
||||
aria-label={t("options.translation.splitTranslatorShortcut.title")}
|
||||
readOnly
|
||||
value={displayShortcut}
|
||||
/>
|
||||
<Button type="button" variant="outline" onClick={handleOpenShortcutSettings}>
|
||||
{t("options.translation.splitTranslatorShortcut.openSettings")}
|
||||
</Button>
|
||||
</div>
|
||||
</ConfigCard>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue