forked from baron/baron-sso
Merge branch 'dev' into feature/rbac-simplification-and-remove-dev-switcher
This commit is contained in:
77
devfront/src/components/common/LanguageSelector.test.tsx
Normal file
77
devfront/src/components/common/LanguageSelector.test.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { act } from "react";
|
||||
import { createRoot, type Root } from "react-dom/client";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("../../lib/i18n", () => ({
|
||||
t: (_key: string, fallback?: string) => fallback ?? _key,
|
||||
}));
|
||||
|
||||
import LanguageSelector from "./LanguageSelector";
|
||||
|
||||
const roots: Root[] = [];
|
||||
|
||||
beforeEach(() => {
|
||||
window.localStorage.clear();
|
||||
window.history.replaceState({}, "", "/");
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
for (const root of roots.splice(0)) {
|
||||
act(() => {
|
||||
root.unmount();
|
||||
});
|
||||
}
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
function renderSelector() {
|
||||
const container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
const root = createRoot(container);
|
||||
roots.push(root);
|
||||
|
||||
act(() => {
|
||||
root.render(<LanguageSelector />);
|
||||
});
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
describe("LanguageSelector", () => {
|
||||
it("prefers the locale stored in localStorage", () => {
|
||||
window.localStorage.setItem("locale", "en");
|
||||
|
||||
const container = renderSelector();
|
||||
const select = container.querySelector("select") as HTMLSelectElement;
|
||||
|
||||
expect(select.value).toBe("en");
|
||||
});
|
||||
|
||||
it("falls back to the path locale when storage is empty", () => {
|
||||
window.history.replaceState({}, "", "/ko");
|
||||
|
||||
const container = renderSelector();
|
||||
const select = container.querySelector("select") as HTMLSelectElement;
|
||||
|
||||
expect(select.value).toBe("ko");
|
||||
});
|
||||
|
||||
it("saves the selected locale and dispatches a development event", () => {
|
||||
vi.stubEnv("MODE", "development");
|
||||
const dispatchEvent = vi.spyOn(window, "dispatchEvent");
|
||||
window.history.replaceState({}, "", "/ko");
|
||||
|
||||
const container = renderSelector();
|
||||
const select = container.querySelector("select") as HTMLSelectElement;
|
||||
|
||||
act(() => {
|
||||
select.value = "en";
|
||||
select.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
});
|
||||
|
||||
expect(window.localStorage.getItem("locale")).toBe("en");
|
||||
expect(dispatchEvent).toHaveBeenCalled();
|
||||
expect(select.value).toBe("en");
|
||||
});
|
||||
});
|
||||
107
devfront/src/components/ui/copy-button.test.tsx
Normal file
107
devfront/src/components/ui/copy-button.test.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import { act } from "react";
|
||||
import { createRoot, type Root } from "react-dom/client";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { CopyButton } from "./copy-button";
|
||||
|
||||
const roots: Root[] = [];
|
||||
|
||||
afterEach(() => {
|
||||
for (const root of roots.splice(0)) {
|
||||
act(() => {
|
||||
root.unmount();
|
||||
});
|
||||
}
|
||||
vi.restoreAllMocks();
|
||||
delete (navigator as Navigator & { clipboard?: unknown }).clipboard;
|
||||
Object.defineProperty(window, "isSecureContext", {
|
||||
value: false,
|
||||
configurable: true,
|
||||
});
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
function renderCopyButton(value: string, onCopy = vi.fn()) {
|
||||
const container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
const root = createRoot(container);
|
||||
roots.push(root);
|
||||
|
||||
act(() => {
|
||||
root.render(<CopyButton value={value} onCopy={onCopy} />);
|
||||
});
|
||||
|
||||
return { container, onCopy };
|
||||
}
|
||||
|
||||
describe("CopyButton", () => {
|
||||
it("copies with the clipboard API when secure context is available", async () => {
|
||||
const writeText = vi.fn().mockResolvedValue(undefined);
|
||||
Object.defineProperty(navigator, "clipboard", {
|
||||
value: { writeText },
|
||||
configurable: true,
|
||||
});
|
||||
Object.defineProperty(window, "isSecureContext", {
|
||||
value: true,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
const { container, onCopy } = renderCopyButton("client-secret");
|
||||
const button = container.querySelector("button");
|
||||
expect(button).not.toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
button?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
|
||||
expect(writeText).toHaveBeenCalledWith("client-secret");
|
||||
expect(onCopy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("falls back to execCommand when clipboard API is unavailable", async () => {
|
||||
const execCommand = vi.fn(() => true);
|
||||
Object.defineProperty(document, "execCommand", {
|
||||
value: execCommand,
|
||||
configurable: true,
|
||||
});
|
||||
Object.defineProperty(window, "isSecureContext", {
|
||||
value: false,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
const { container, onCopy } = renderCopyButton("client-secret");
|
||||
const button = container.querySelector("button");
|
||||
expect(button).not.toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
button?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
|
||||
expect(execCommand).toHaveBeenCalledWith("copy");
|
||||
expect(onCopy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("keeps running when the fallback copy flow fails", async () => {
|
||||
const execCommand = vi.fn(() => false);
|
||||
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
Object.defineProperty(document, "execCommand", {
|
||||
value: execCommand,
|
||||
configurable: true,
|
||||
});
|
||||
Object.defineProperty(window, "isSecureContext", {
|
||||
value: false,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
const { container, onCopy } = renderCopyButton("client-secret");
|
||||
const button = container.querySelector("button");
|
||||
expect(button).not.toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
button?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
|
||||
expect(execCommand).toHaveBeenCalledWith("copy");
|
||||
expect(onCopy).not.toHaveBeenCalled();
|
||||
expect(errorSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user