diff --git a/apps/readest-app/src/__tests__/app/reader/components/SectionInfo.test.tsx b/apps/readest-app/src/__tests__/app/reader/components/SectionInfo.test.tsx new file mode 100644 index 00000000..9d74d524 --- /dev/null +++ b/apps/readest-app/src/__tests__/app/reader/components/SectionInfo.test.tsx @@ -0,0 +1,70 @@ +import { describe, it, expect, vi } from 'vitest'; +import { render } from '@testing-library/react'; +import SectionInfo from '@/app/reader/components/SectionInfo'; + +vi.mock('@/context/EnvContext', () => ({ + useEnv: () => ({ appService: { isAndroidApp: false, isMobile: true } }), +})); + +vi.mock('@/store/themeStore', () => ({ + useThemeStore: () => ({ systemUIVisible: false, statusBarHeight: 0 }), +})); + +vi.mock('@/store/readerStore', () => ({ + useReaderStore: () => ({ + hoveredBookKey: '', + getView: () => null, + getViewSettings: () => ({ marginTopPx: 44 }), + setHoveredBookKey: vi.fn(), + }), +})); + +vi.mock('@/hooks/useTranslation', () => ({ + useTranslation: () => (key: string) => key, +})); + +const baseProps = { + bookKey: 'book-1', + section: 'Chapter 1', + showDoubleBorder: false, + isScrolled: true, + isVertical: false, + isEink: false, + horizontalGap: 5, + contentInsets: { top: 89, right: 0, bottom: 0, left: 0 }, + gridInsets: { top: 45, right: 0, bottom: 0, left: 0 }, +}; + +describe('SectionInfo notch mask', () => { + it('spans the grid cell and clips to the top inset so its texture aligns with the viewer (#4486)', () => { + // The scrolled-mode notch mask hides content scrolling under the status bar, + // but must not occlude the background texture. Its texture ::before only + // tile-aligns with .foliate-viewer::before (background-size cover/contain + // resolves against the element box) if the notch shares the viewer's paint + // box: full grid cell, with the visible/hit area clipped to the inset strip. + const { container } = render(); + const notch = container.querySelector('.notch-area') as HTMLElement; + + expect(notch).not.toBeNull(); + expect(notch.classList.contains('inset-0')).toBe(true); + expect(notch.classList.contains('notch-masked')).toBe(true); + expect(notch.classList.contains('bg-base-100')).toBe(true); + expect(notch.style.clipPath).toBe('inset(0 0 calc(100% - 45px) 0)'); + }); + + it('keeps the notch transparent and untextured in paginated mode', () => { + const { container } = render(); + const notch = container.querySelector('.notch-area') as HTMLElement; + + expect(notch.classList.contains('notch-masked')).toBe(false); + expect(notch.classList.contains('bg-base-100')).toBe(false); + }); + + it('keeps the notch transparent and untextured in vertical scrolled mode', () => { + const { container } = render(); + const notch = container.querySelector('.notch-area') as HTMLElement; + + expect(notch.classList.contains('notch-masked')).toBe(false); + expect(notch.classList.contains('bg-base-100')).toBe(false); + }); +}); diff --git a/apps/readest-app/src/__tests__/styles/textures.test.ts b/apps/readest-app/src/__tests__/styles/textures.test.ts new file mode 100644 index 00000000..21c4d609 --- /dev/null +++ b/apps/readest-app/src/__tests__/styles/textures.test.ts @@ -0,0 +1,19 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { mountBackgroundTexture, unmountBackgroundTexture } from '@/styles/textures'; + +afterEach(() => { + unmountBackgroundTexture(document); +}); + +describe('mountBackgroundTexture', () => { + it('covers the scrolled-mode notch mask so the top inset strip is textured (#4486)', () => { + mountBackgroundTexture(document, { + id: 'paper', + name: 'Paper', + url: '/images/paper-texture.png', + }); + + const style = document.getElementById('background-texture'); + expect(style?.textContent).toContain('.notch-masked::before'); + }); +}); diff --git a/apps/readest-app/src/app/reader/components/SectionInfo.tsx b/apps/readest-app/src/app/reader/components/SectionInfo.tsx index c466684a..7cc4938c 100644 --- a/apps/readest-app/src/app/reader/components/SectionInfo.tsx +++ b/apps/readest-app/src/app/reader/components/SectionInfo.tsx @@ -56,14 +56,20 @@ const SectionInfo: React.FC = ({ <>
{ } body::before, .sidebar-container::before, .notebook-container::before, - .foliate-viewer::before { + .foliate-viewer::before, .notch-masked::before { content: ""; position: absolute; top: 0;