import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react"; import { useState } from "react"; import { afterEach, describe, expect, it } from "vitest"; import { vi } from "vitest"; import type { WordDocument } from "../../types"; import { QWord, qwordCurrentPageFromViewport } from "./QWord"; afterEach(() => { cleanup(); }); describe("qwordCurrentPageFromViewport", () => { it("вычисляет текущую страницу по положению листа в области просмотра", () => { expect(qwordCurrentPageFromViewport({ stageTop: 34, paperTop: 227, pageHeight: 1123, pageCount: 3, zoom: 1 })).toBe(1); expect(qwordCurrentPageFromViewport({ stageTop: 34, paperTop: -1090, pageHeight: 1123, pageCount: 3, zoom: 1 })).toBe(2); expect(qwordCurrentPageFromViewport({ stageTop: 34, paperTop: -6000, pageHeight: 1123, pageCount: 3, zoom: 1 })).toBe(3); }); it("учитывает масштаб при расчете текущей страницы", () => { expect(qwordCurrentPageFromViewport({ stageTop: 34, paperTop: -1200, pageHeight: 1123, pageCount: 3, zoom: 1 })).toBe(2); expect(qwordCurrentPageFromViewport({ stageTop: 34, paperTop: -1200, pageHeight: 1123, pageCount: 3, zoom: 1.2 })).toBe(1); }); }); describe("QWord", () => { it("переключает вкладки ленты и показывает команды выбранной вкладки", () => { const originalResizeObserver = globalThis.ResizeObserver; const originalRequestAnimationFrame = window.requestAnimationFrame; globalThis.ResizeObserver = class { observe() {} disconnect() {} } as unknown as typeof ResizeObserver; window.requestAnimationFrame = ((callback: FrameRequestCallback) => { callback(0); return 1; }) as typeof window.requestAnimationFrame; const document: WordDocument = { id: "word-ribbon-tabs-test", title: "Лента.docx", updatedAt: "2026-06-02T00:00:00.000Z", html: "

Текст

" }; try { render(); expect(screen.getByRole("tab", { name: "Главная" })).toHaveAttribute("aria-selected", "true"); expect(screen.getByRole("region", { name: "Шрифт" })).toBeInTheDocument(); fireEvent.click(screen.getByRole("tab", { name: "Вставка" })); expect(screen.getByRole("tab", { name: "Вставка" })).toHaveAttribute("aria-selected", "true"); expect(screen.getByRole("region", { name: "Вставка" })).toBeInTheDocument(); expect(screen.queryByRole("region", { name: "Шрифт" })).not.toBeInTheDocument(); fireEvent.click(screen.getByRole("tab", { name: "Файл" })); expect(screen.getByRole("tab", { name: "Файл" })).toHaveAttribute("aria-selected", "true"); expect(screen.getByRole("region", { name: "Файл" })).toBeInTheDocument(); expect(screen.queryByRole("region", { name: "Вставка" })).not.toBeInTheDocument(); } finally { globalThis.ResizeObserver = originalResizeObserver; window.requestAnimationFrame = originalRequestAnimationFrame; } }); it("переключает режим просмотра документа на вкладке Вид", () => { const originalResizeObserver = globalThis.ResizeObserver; const originalRequestAnimationFrame = window.requestAnimationFrame; globalThis.ResizeObserver = class { observe() {} disconnect() {} } as unknown as typeof ResizeObserver; window.requestAnimationFrame = ((callback: FrameRequestCallback) => { callback(0); return 1; }) as typeof window.requestAnimationFrame; const document: WordDocument = { id: "word-view-mode-test", title: "Вид.docx", updatedAt: "2026-06-02T00:00:00.000Z", html: "

Текст

" }; try { render(); const stage = screen.getByLabelText("Редактор QWord"); expect(stage).toHaveClass("qword-view-print"); fireEvent.click(screen.getByRole("tab", { name: "Вид" })); fireEvent.click(screen.getByRole("button", { name: "Веб-документ" })); expect(stage).toHaveClass("qword-view-web"); expect(screen.getByRole("button", { name: "Веб-документ" })).toHaveAttribute("aria-pressed", "true"); } finally { globalThis.ResizeObserver = originalResizeObserver; window.requestAnimationFrame = originalRequestAnimationFrame; } }); it("вставляет таблицу выбранного размера через вкладку Вставка", () => { const originalResizeObserver = globalThis.ResizeObserver; const originalRequestAnimationFrame = window.requestAnimationFrame; const onChange = vi.fn(); globalThis.ResizeObserver = class { observe() {} disconnect() {} } as unknown as typeof ResizeObserver; window.requestAnimationFrame = ((callback: FrameRequestCallback) => { callback(0); return 1; }) as typeof window.requestAnimationFrame; const document: WordDocument = { id: "word-insert-table-test", title: "Таблица.docx", updatedAt: "2026-06-02T00:00:00.000Z", html: "

Перед таблицей

" }; try { render(); fireEvent.click(screen.getByRole("tab", { name: "Вставка" })); fireEvent.change(screen.getByLabelText("Строки таблицы"), { target: { value: "2" } }); fireEvent.change(screen.getByLabelText("Столбцы таблицы"), { target: { value: "3" } }); fireEvent.click(screen.getByRole("button", { name: "Таблица" })); const updatedDocument = onChange.mock.calls[onChange.mock.calls.length - 1]?.[0] as WordDocument | undefined; expect(updatedDocument?.html).toContain(""); expect(updatedDocument?.html.match(/ { const originalResizeObserver = globalThis.ResizeObserver; const originalRequestAnimationFrame = window.requestAnimationFrame; const onChange = vi.fn(); globalThis.ResizeObserver = class { observe() {} disconnect() {} } as unknown as typeof ResizeObserver; window.requestAnimationFrame = ((callback: FrameRequestCallback) => { callback(0); return 1; }) as typeof window.requestAnimationFrame; const document: WordDocument = { id: "word-insert-table-restore-test", title: "Активная таблица.docx", updatedAt: "2026-06-02T00:00:00.000Z", html: "

Перед таблицей

" }; function StatefulQWord() { const [currentDocument, setCurrentDocument] = useState(document); return ( { onChange(nextDocument); setCurrentDocument(nextDocument); }} /> ); } try { render(); fireEvent.click(screen.getByRole("tab", { name: "Вставка" })); const tableButton = screen.getByRole("button", { name: "Таблица" }); expect(fireEvent.mouseDown(tableButton)).toBe(false); fireEvent.click(tableButton); const rowButton = screen.getByRole("button", { name: "Строка ниже" }); expect(fireEvent.mouseDown(rowButton)).toBe(false); fireEvent.click(rowButton); const updatedDocument = onChange.mock.calls[onChange.mock.calls.length - 1]?.[0] as WordDocument | undefined; expect(updatedDocument?.html.match(/ { const originalResizeObserver = globalThis.ResizeObserver; const originalRequestAnimationFrame = window.requestAnimationFrame; const onChange = vi.fn(); globalThis.ResizeObserver = class { observe() {} disconnect() {} } as unknown as typeof ResizeObserver; window.requestAnimationFrame = ((callback: FrameRequestCallback) => { callback(0); return 1; }) as typeof window.requestAnimationFrame; const document: WordDocument = { id: "word-table-structure-test", title: "Структура таблицы.docx", updatedAt: "2026-06-02T00:00:00.000Z", html: "
AB
" }; function selectCell(text: string) { const cell = screen.getByText(text).closest("td"); expect(cell).toBeInTheDocument(); const range = window.document.createRange(); range.selectNodeContents(cell as HTMLTableCellElement); window.getSelection()?.removeAllRanges(); window.getSelection()?.addRange(range); } function StatefulQWord() { const [currentDocument, setCurrentDocument] = useState(document); return ( { onChange(nextDocument); setCurrentDocument(nextDocument); }} /> ); } try { render(); fireEvent.click(screen.getByRole("tab", { name: "Вставка" })); selectCell("A"); const rowButton = screen.getByRole("button", { name: "Строка ниже" }); expect(fireEvent.mouseDown(rowButton)).toBe(false); fireEvent.click(rowButton); let updatedDocument = onChange.mock.calls[onChange.mock.calls.length - 1]?.[0] as WordDocument | undefined; expect(updatedDocument?.html.match(/ { const originalResizeObserver = globalThis.ResizeObserver; const originalRequestAnimationFrame = window.requestAnimationFrame; const onStatusChange = vi.fn(); globalThis.ResizeObserver = class { observe() {} disconnect() {} } as unknown as typeof ResizeObserver; window.requestAnimationFrame = ((callback: FrameRequestCallback) => { callback(0); return 1; }) as typeof window.requestAnimationFrame; const document: WordDocument = { id: "word-scroll-test", title: "Прокрутка.docx", updatedAt: "2026-06-02T00:00:00.000Z", html: "

Первая страница

" }; try { const view = render(); const stage = view.getByLabelText("Редактор QWord"); const paper = view.getByLabelText("Содержимое документа"); Object.defineProperty(paper, "scrollHeight", { configurable: true, value: 2400 }); stage.getBoundingClientRect = () => ({ bottom: 884, height: 850, left: 0, right: 1008, top: 34, width: 1008, x: 0, y: 34, toJSON: () => ({}) }); paper.getBoundingClientRect = () => ({ bottom: -77, height: 1123, left: 107, right: 901, top: -1200, width: 794, x: 107, y: -1200, toJSON: () => ({}) }); fireEvent.scroll(stage); await waitFor(() => expect(onStatusChange).toHaveBeenLastCalledWith({ currentPage: 2, pageCount: 3, wordCount: 2 })); } finally { globalThis.ResizeObserver = originalResizeObserver; window.requestAnimationFrame = originalRequestAnimationFrame; } }); });