diff --git a/apps/client/src/features/editor/components/fixed-toolbar/groups/dictation-group.test.tsx b/apps/client/src/features/editor/components/fixed-toolbar/groups/dictation-group.test.tsx new file mode 100644 index 00000000..6aaf6983 --- /dev/null +++ b/apps/client/src/features/editor/components/fixed-toolbar/groups/dictation-group.test.tsx @@ -0,0 +1,75 @@ +import { describe, it, expect, vi } from "vitest"; +import { useEffect, useState } from "react"; +import { render, act } from "@testing-library/react"; + +// Regression test for #311: on a page the user can edit, the byline mic stayed +// stuck disabled until an unrelated re-render happened, because DictationGroup +// read the non-reactive field `editor.isEditable` directly. The fix reads it via +// `useEditorState`, which subscribes to the editor's own events. +// +// The mock below mirrors the real `useEditorState` contract: it runs the +// selector, and re-runs it (re-rendering the consumer) whenever the editor emits +// an event. This is what makes the test faithful — with the pre-fix code +// (`disabled={!editor.isEditable}`) DictationGroup never subscribes, so emitting +// an event would NOT re-render and the mic would stay disabled. +vi.mock("@tiptap/react", () => ({ + useEditorState: ({ editor, selector }: any) => { + const [value, setValue] = useState(() => selector({ editor })); + useEffect(() => { + const handler = () => setValue(selector({ editor })); + editor.on("update", handler); + return () => editor.off("update", handler); + }, [editor, selector]); + return value; + }, +})); + +// The mic only cares about the workspace's streaming flag; return a stable stub. +vi.mock("jotai", () => ({ + useAtomValue: () => ({ settings: { ai: { dictationStreaming: false } } }), +})); +vi.mock("@/features/user/atoms/current-user-atom.ts", () => ({ + workspaceAtom: {}, +})); + +// Detectable stand-in that surfaces the `disabled` prop the component computes. +vi.mock("@/features/dictation/components/mic-button", () => ({ + MicButton: ({ disabled }: any) => ( +