Files
OwnCord/Client/tauri-client/tests/unit/chat-header.test.ts
T
jevb 867041b094 test: client test updates — fix failures + align with security hardening
Update 111 test files to match security hardening changes:
- acceptInvalidCerts now conditional on allowSelfSigned
- Credential store no longer returns passwords over IPC
- File type validation uses strict MIME allowlist
- Search rate limiter timing adjustments
- Dispatcher cleanup mock additions
- Audio elements screenshare mute preservation

2962 tests passing across 110 test files.
2026-04-01 11:40:31 +02:00

243 lines
7.1 KiB
TypeScript

import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { buildChatHeader, updateChatHeaderForDm } from "../../src/pages/main-page/ChatHeader";
describe("ChatHeader", () => {
let container: HTMLDivElement;
beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
container.remove();
});
it("renders the chat header element", () => {
const { element } = buildChatHeader({
onTogglePins: vi.fn(),
});
container.appendChild(element);
expect(container.querySelector('[data-testid="chat-header"]')).not.toBeNull();
});
it("displays default channel name", () => {
const { element, refs } = buildChatHeader({
onTogglePins: vi.fn(),
});
container.appendChild(element);
expect(refs.nameEl.textContent).toBe("general");
expect(container.querySelector('[data-testid="chat-header-name"]')?.textContent).toBe(
"general",
);
});
it("displays hash prefix", () => {
const { element } = buildChatHeader({
onTogglePins: vi.fn(),
});
container.appendChild(element);
const hash = container.querySelector(".ch-hash");
expect(hash?.textContent).toBe("#");
});
it("contains a search input", () => {
const { element } = buildChatHeader({
onTogglePins: vi.fn(),
});
container.appendChild(element);
const searchInput = container.querySelector(".search-input") as HTMLInputElement;
expect(searchInput).not.toBeNull();
expect(searchInput.placeholder).toBe("Search...");
});
it("calls onTogglePins when pin button is clicked", () => {
const onTogglePins = vi.fn();
const { element } = buildChatHeader({
onTogglePins,
});
container.appendChild(element);
const pinBtn = container.querySelector('[data-testid="pin-btn"]') as HTMLButtonElement;
pinBtn.click();
expect(onTogglePins).toHaveBeenCalledOnce();
});
it("provides mutable refs for channel name and topic", () => {
const { element, refs } = buildChatHeader({
onTogglePins: vi.fn(),
});
container.appendChild(element);
// Update name via ref
refs.nameEl.textContent = "announcements";
expect(container.querySelector('[data-testid="chat-header-name"]')?.textContent).toBe(
"announcements",
);
// Update topic via ref
refs.topicEl.textContent = "Important news";
expect(container.querySelector(".ch-topic")?.textContent).toBe("Important news");
});
it("has proper aria labels on buttons", () => {
const { element } = buildChatHeader({
onTogglePins: vi.fn(),
});
container.appendChild(element);
const pinBtn = container.querySelector('[data-testid="pin-btn"]');
expect(pinBtn?.getAttribute("aria-label")).toBe("Pins");
});
it("calls onSearchFocus and blurs input when search is focused", () => {
const onSearchFocus = vi.fn();
const { element } = buildChatHeader({
onTogglePins: vi.fn(),
onSearchFocus,
});
container.appendChild(element);
const searchInput = container.querySelector('[data-testid="search-input"]') as HTMLInputElement;
const blurSpy = vi.spyOn(searchInput, "blur");
searchInput.dispatchEvent(new Event("focus"));
expect(onSearchFocus).toHaveBeenCalledOnce();
expect(blurSpy).toHaveBeenCalledOnce();
});
it("does not add focus listener when onSearchFocus is not provided", () => {
const { element } = buildChatHeader({
onTogglePins: vi.fn(),
// no onSearchFocus
});
container.appendChild(element);
const searchInput = container.querySelector('[data-testid="search-input"]') as HTMLInputElement;
const blurSpy = vi.spyOn(searchInput, "blur");
// Focus should not cause blur since no handler was registered
searchInput.dispatchEvent(new Event("focus"));
expect(blurSpy).not.toHaveBeenCalled();
});
it("contains a pin icon inside the pin button", () => {
const { element } = buildChatHeader({
onTogglePins: vi.fn(),
});
container.appendChild(element);
const pinBtn = container.querySelector('[data-testid="pin-btn"]');
// The button should contain an SVG icon element
expect(pinBtn?.children.length).toBeGreaterThan(0);
});
it("contains a divider element", () => {
const { element } = buildChatHeader({
onTogglePins: vi.fn(),
});
container.appendChild(element);
const divider = container.querySelector(".ch-divider");
expect(divider).not.toBeNull();
});
it("topic element starts empty", () => {
const { refs } = buildChatHeader({
onTogglePins: vi.fn(),
});
expect(refs.topicEl.textContent).toBe("");
});
});
// ---------------------------------------------------------------------------
// updateChatHeaderForDm
// ---------------------------------------------------------------------------
describe("updateChatHeaderForDm", () => {
let container: HTMLDivElement;
beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
container.remove();
});
it("sets @ prefix and username for DM recipient", () => {
const { element, refs } = buildChatHeader({
onTogglePins: vi.fn(),
});
container.appendChild(element);
updateChatHeaderForDm(refs, { username: "Alice", status: "online" });
expect(refs.hashEl.textContent).toBe("@");
expect(refs.nameEl.textContent).toBe("Alice");
expect(refs.topicEl.textContent).toBe("online");
});
it("sets status text as topic for DM", () => {
const { element, refs } = buildChatHeader({
onTogglePins: vi.fn(),
});
container.appendChild(element);
updateChatHeaderForDm(refs, { username: "Bob", status: "idle" });
expect(refs.topicEl.textContent).toBe("idle");
});
it("resets to # when recipient is null", () => {
const { element, refs } = buildChatHeader({
onTogglePins: vi.fn(),
});
container.appendChild(element);
// First set to DM mode
updateChatHeaderForDm(refs, { username: "Alice", status: "online" });
expect(refs.hashEl.textContent).toBe("@");
// Then reset to channel mode
updateChatHeaderForDm(refs, null);
expect(refs.hashEl.textContent).toBe("#");
});
it("does not change name or topic when recipient is null", () => {
const { element, refs } = buildChatHeader({
onTogglePins: vi.fn(),
});
container.appendChild(element);
// Set DM first
updateChatHeaderForDm(refs, { username: "Alice", status: "online" });
// Reset — only hash changes, name/topic keep their values from setText
updateChatHeaderForDm(refs, null);
expect(refs.hashEl.textContent).toBe("#");
// Name and topic are NOT reset by updateChatHeaderForDm(null) — only hash
});
it("handles recipient with empty status", () => {
const { element, refs } = buildChatHeader({
onTogglePins: vi.fn(),
});
container.appendChild(element);
updateChatHeaderForDm(refs, { username: "Charlie", status: "" });
expect(refs.hashEl.textContent).toBe("@");
expect(refs.nameEl.textContent).toBe("Charlie");
expect(refs.topicEl.textContent).toBe("");
});
});