Files
reader/web/test/reader-indicators.test.ts
T

55 lines
2.9 KiB
TypeScript

import { render } from '../src/shared/ui/render-root.ts';
// @vitest-environment jsdom
import { afterEach, describe, expect, it, vi } from 'vitest';
import { act } from 'react';
import { createElement as h } from 'react';
import { ReaderIndicators } from '../src/ui/reader-indicators.tsx';
import type { ChromeState } from '../src/ui/reader-chrome.tsx';
import { SettingsStore } from '../src/store/settings.ts';
const container = document.createElement('div');
const state = {
title: '测试书名', chapterLabel: '第二章', pageInChapter: 3, chapterPages: 12, progress: 0.47,
} as ChromeState;
const texts = () => Array.from(container.querySelectorAll('span'), el => el.textContent);
afterEach(() => { act(() => render(null, container)); vi.useRealTimers(); });
describe('reading information', () => {
it('maps all four positions independently and allows hiding each one', () => {
act(() => render(h(ReaderIndicators, { state: { ...state,
readoutTopLeft: 'book', readoutTopRight: 'chapter',
readoutBottomLeft: 'progress', readoutBottomRight: 'none',
} }), container));
expect(texts()).toEqual(['测试书名', '第二章', '第3/12页 47%', '']);
act(() => render(h(ReaderIndicators, { state: { ...state,
readoutTopLeft: 'none', readoutTopRight: 'none',
readoutBottomLeft: 'none', readoutBottomRight: 'none',
} }), container));
expect(texts()).toEqual(['', '', '', '']);
expect(container.querySelectorAll('.reading-indicator')).toHaveLength(2);
});
it('refreshes time at the minute boundary and on resume, then cleans up', () => {
vi.useFakeTimers();
vi.setSystemTime(new Date(2026, 8, 20, 9, 5, 59));
act(() => render(h(ReaderIndicators, { state }), container));
expect(texts()[3]).toBe('09:05');
act(() => { vi.advanceTimersByTime(1000); });
expect(texts()[3]).toBe('09:06');
vi.setSystemTime(new Date(2026, 8, 20, 10, 15));
act(() => { document.dispatchEvent(new Event('visibilitychange')); });
expect(texts()[3]).toBe('10:15');
act(() => render(null, container));
expect(vi.getTimerCount()).toBe(0);
});
it('migrates older preferences and persists each position across store instances', async () => {
let saved = JSON.stringify({ theme: 'sepia', readoutTopRight: 'invalid' });
const kv = { get: async () => saved, set: async (_key: string, value: string) => { saved = value; }, remove: async () => {} };
const store = new SettingsStore(kv);
expect(await store.load()).toMatchObject({ theme: 'sepia', readoutTopLeft: 'chapter', readoutTopRight: 'none', readoutBottomLeft: 'progress', readoutBottomRight: 'time' });
await store.update({ readoutTopLeft: 'time', readoutTopRight: 'book', readoutBottomLeft: 'none', readoutBottomRight: 'chapter' });
expect(await new SettingsStore(kv).load()).toMatchObject({ readoutTopLeft: 'time', readoutTopRight: 'book', readoutBottomLeft: 'none', readoutBottomRight: 'chapter' });
});
});