wrolpi/app/src/setupTests.js
Roland 851485ef1b ui: Common.js and thirteen more files off Semantic
Phase 2, part 3.  Baseline 63 -> 50.

Common.js is the one that mattered: 51 files import it, so its Semantic usage
was propping up the rest of the migration.  Everything in it now comes from the
library -- Paginator, the two search inputs, DirectorySearch, TabLinks, the
toggles, the messages, APIButton, Breadcrumbs, SimpleAccordion, IframeViewer,
CardPoster, FileIcon, LoadStatistic, the theme menu, and the hotspot icons.

Also migrated: Placeholder, Apps, ArchivePreview, CbzViewer, AddToPlaylist,
TaggedDeleteConfirmModal, the collection edit/tag forms, and four inventory
components.

New in the library, all in the gallery and tested:

  SearchBox     Semantic's `<Search category>` with its grouped results shape
                kept, because four call sites already build it.  A real
                combobox rather than a div that listens for clicks.
  Pagination    Semantic's prop names; onPageChange gets the page number.
  TabBar        the strip only -- routing stays with the caller.
  IconStack     Semantic's IconGroup, named once for assistive technology.
  Table.HeaderCell sorted/onSort -- a real button carrying aria-sort.
  Button/IconButton translate Semantic's size names.

That last one is defensive: unmigrated callers still pass size='tiny', and
Mantine drops a size it does not recognise without a word, so the button
quietly renders at the wrong size.  Mapping here keeps the mixed state honest.

Three real bugs found and fixed on the way, none of them cosmetic:

  * `fileSuffixIconName` returned fourteen icon names the map did not have, so
    opening a folder with a PDF, a Word document or an archive in it logged an
    error and drew a fallback glyph.  All fourteen mapped, with a test that
    walks the mapper's own return values, and another asserting every mapped
    name resolves to a Tabler component that exists.
  * `Statistic` relied on Semantic's `Statistic.Group` for its spacing, so on
    the dashboard the load averages ran together into "1 MIN. LOAD5 MIN.
    LOAD15 MIN. LOAD".  It carries its own spacing now.
  * Semantic's inverted stylesheet colors every descendant of an inverted
    segment, so Toggle labels inherited #dddddd -- white pixels in night mode,
    on the Settings page, which is exactly where someone adjusts their WROLPi
    in the dark.  Restated from tokens.

The baseline ratchet gained a third assertion: a migrated file may still lean
on Semantic's CSS classes without importing the library, which the import check
cannot see and which would collapse silently when Phase 4 deletes the
stylesheet.  There is one such bridge today and it is now declared.
2026-07-30 11:55:39 -06:00

86 lines
2.1 KiB
JavaScript

// Jest setup file for React Testing Library
// This file is automatically loaded by Create React App before running tests
// Polyfill TextEncoder/TextDecoder for react-router v7 compatibility
// See: https://github.com/remix-run/react-router/issues/12363
import { TextDecoder, TextEncoder } from 'util';
global.TextEncoder = TextEncoder;
global.TextDecoder = TextDecoder;
import '@testing-library/jest-dom';
// Set global Jest timeout to 30 seconds
// Tests that exceed this will fail with a clear error message identifying the failing test
jest.setTimeout(30000);
// Mock react-stl-viewer to avoid loading heavy three.js dependency in tests
jest.mock('react-stl-viewer', () => ({
StlViewer: () => null,
}));
// Mock window.matchMedia (used by Semantic UI Media components)
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
// Mock IntersectionObserver (if needed for lazy loading)
global.IntersectionObserver = class IntersectionObserver {
constructor() {
}
disconnect() {
}
observe() {
}
takeRecords() {
return [];
}
unobserve() {
}
};
// Mock ResizeObserver (used by Mantine's ScrollArea, e.g. inside Select/Combobox dropdowns).
global.ResizeObserver = class ResizeObserver {
constructor() {
}
disconnect() {
}
observe() {
}
unobserve() {
}
};
// Suppress console errors during tests (optional - uncomment if needed)
// const originalError = console.error;
// beforeAll(() => {
// console.error = (...args) => {
// if (
// typeof args[0] === 'string' &&
// args[0].includes('Warning: ReactDOM.render')
// ) {
// return;
// }
// originalError.call(console, ...args);
// };
// });
//
// afterAll(() => {
// console.error = originalError;
// });