## Changes ### Cancel button (✕) - New cancel button appears during recording next to stop button - Tapping ✕ stops recording and **discards audio** — no text inserted - Tapping ⏹ stops recording and **inserts text** (as before) ### Question prompt support - Mic button injected into `[data-slot=question-custom-input]` (custom answer textarea) - Text insertion via `selectionStart`/`selectionEnd` (textarea ≠ contenteditable) - MutationObserver detects when question prompt appears ### Solid timer - Timer background: `#e53935` (opaque red) with white text - Was `rgba(229, 57, 53, 0.1)` (semi-transparent — text showed through) - Now fully visible over any text ### Technical - Refactored UI to use CSS classes instead of single IDs (supports multiple buttons) - `insertText()` now takes `target: "composer" | "question"` - 47 tests, 100% line coverage Co-authored-by: opencode-agent <agent@slaid098.dev>
113 lines
3.3 KiB
TypeScript
113 lines
3.3 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { insertText, isOpencodePage, submitPrompt } from "../src/insert.js";
|
|
|
|
beforeEach(() => {
|
|
document.execCommand = vi.fn(() => true);
|
|
});
|
|
|
|
describe("isOpencodePage", () => {
|
|
it("should return false when prompt-input not present", () => {
|
|
document.body.innerHTML = "";
|
|
expect(isOpencodePage()).toBe(false);
|
|
});
|
|
|
|
it("should return true when prompt-input is present", () => {
|
|
document.body.innerHTML =
|
|
'<div data-component="prompt-input" contenteditable="true" role="textbox"></div>';
|
|
expect(isOpencodePage()).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("insertText", () => {
|
|
beforeEach(() => {
|
|
document.body.innerHTML =
|
|
'<div data-component="prompt-input" contenteditable="true" role="textbox"></div>';
|
|
});
|
|
|
|
afterEach(() => {
|
|
document.body.innerHTML = "";
|
|
});
|
|
|
|
it("should return false when input not found", () => {
|
|
document.body.innerHTML = "";
|
|
expect(insertText("hello")).toBe(false);
|
|
});
|
|
|
|
it("should call execCommand with insertText", () => {
|
|
const result = insertText("hello world");
|
|
expect(result).toBe(true);
|
|
expect(document.execCommand).toHaveBeenCalledWith("insertText", false, "hello world");
|
|
});
|
|
|
|
it("should dispatch input event", () => {
|
|
let eventCount = 0;
|
|
const input = document.querySelector('[data-component="prompt-input"]') as HTMLElement;
|
|
input.addEventListener("input", () => {
|
|
eventCount++;
|
|
});
|
|
|
|
insertText("test text");
|
|
expect(eventCount).toBeGreaterThanOrEqual(1);
|
|
});
|
|
});
|
|
|
|
describe("submitPrompt", () => {
|
|
afterEach(() => {
|
|
document.body.innerHTML = "";
|
|
});
|
|
|
|
it("should return false when submit button not found", () => {
|
|
expect(submitPrompt()).toBe(false);
|
|
});
|
|
|
|
it("should click submit button when found", () => {
|
|
let clicked = false;
|
|
const btn = document.createElement("button");
|
|
btn.setAttribute("data-action", "prompt-submit");
|
|
btn.addEventListener("click", () => {
|
|
clicked = true;
|
|
});
|
|
document.body.appendChild(btn);
|
|
|
|
expect(submitPrompt()).toBe(true);
|
|
expect(clicked).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("insertText into question textarea", () => {
|
|
afterEach(() => {
|
|
document.body.innerHTML = "";
|
|
});
|
|
|
|
it("should return false when question textarea not found", () => {
|
|
expect(insertText("hello", "question")).toBe(false);
|
|
});
|
|
|
|
it("should insert text into textarea at cursor position", () => {
|
|
const textarea = document.createElement("textarea");
|
|
textarea.setAttribute("data-slot", "question-custom-input");
|
|
textarea.value = "hello world";
|
|
textarea.selectionStart = 6;
|
|
textarea.selectionEnd = 11;
|
|
document.body.appendChild(textarea);
|
|
|
|
const result = insertText("there", "question");
|
|
expect(result).toBe(true);
|
|
expect(textarea.value).toBe("hello there");
|
|
expect(textarea.selectionStart).toBe(11);
|
|
});
|
|
|
|
it("should dispatch input event", () => {
|
|
let eventCount = 0;
|
|
const textarea = document.createElement("textarea");
|
|
textarea.setAttribute("data-slot", "question-custom-input");
|
|
textarea.value = "";
|
|
textarea.addEventListener("input", () => {
|
|
eventCount++;
|
|
});
|
|
document.body.appendChild(textarea);
|
|
|
|
insertText("test", "question");
|
|
expect(eventCount).toBeGreaterThanOrEqual(1);
|
|
});
|
|
});
|