test: expand honest whole-repo coverage

Adds broad coverage across stores, hooks, and runtime utilities while switching `vitest.config.ts` to an explicit whole-repo include list. This turns the coverage report into a real repo-wide baseline instead of an imported-file subset.
This commit is contained in:
plebeius
2026-03-08 13:59:38 +08:00
parent 7da0df9466
commit 76eb4b537e
18 changed files with 1635 additions and 0 deletions
@@ -0,0 +1,59 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
const testState = vi.hoisted(() => {
const i18next = {
init: vi.fn(),
use: vi.fn(),
};
i18next.use.mockImplementation(() => i18next);
return {
backendPlugin: { name: 'backend-plugin' },
detectorPlugin: { name: 'detector-plugin' },
i18next,
reactPlugin: { name: 'react-plugin' },
};
});
vi.mock('i18next', () => ({
default: testState.i18next,
}));
vi.mock('i18next-http-backend', () => ({
default: testState.backendPlugin,
}));
vi.mock('i18next-browser-languagedetector', () => ({
default: testState.detectorPlugin,
}));
vi.mock('react-i18next', () => ({
initReactI18next: testState.reactPlugin,
}));
describe('init-translations', () => {
beforeEach(() => {
vi.resetModules();
testState.i18next.use.mockClear();
testState.i18next.init.mockClear();
testState.i18next.use.mockImplementation(() => testState.i18next);
});
it('initializes i18next with the backend, detector, react plugin, and supported languages', async () => {
await import('../init-translations');
expect(testState.i18next.use).toHaveBeenNthCalledWith(1, testState.backendPlugin);
expect(testState.i18next.use).toHaveBeenNthCalledWith(2, testState.detectorPlugin);
expect(testState.i18next.use).toHaveBeenNthCalledWith(3, testState.reactPlugin);
expect(testState.i18next.init).toHaveBeenCalledWith(
expect.objectContaining({
fallbackLng: 'en',
ns: ['default'],
defaultNS: 'default',
backend: { loadPath: './translations/{{lng}}/{{ns}}.json' },
}),
);
expect(testState.i18next.init.mock.calls[0]?.[0]?.supportedLngs).toContain('en');
expect(testState.i18next.init.mock.calls[0]?.[0]?.supportedLngs).toContain('ja');
});
});
+47
View File
@@ -0,0 +1,47 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
describe('snow', () => {
beforeEach(() => {
document.head.innerHTML = '';
document.body.innerHTML = '';
vi.restoreAllMocks();
vi.useRealTimers();
vi.resetModules();
});
it('creates and removes a deterministic snow field', async () => {
const mathRandomSpy = vi.spyOn(Math, 'random').mockReturnValue(0.5);
const { initSnow, removeSnow } = await import('../snow');
initSnow({ flakeCount: 3 });
const snowfield = document.getElementById('js-snowfield');
expect(snowfield).toBeTruthy();
expect(snowfield?.children).toHaveLength(2);
expect(document.head.querySelector('style')?.textContent).toContain('fall-1');
removeSnow();
expect(document.getElementById('js-snowfield')).toBeNull();
mathRandomSpy.mockRestore();
});
it('prefers the special theme store and otherwise falls back to christmas dates', async () => {
const useSpecialThemeStore = (await import('../../stores/use-special-theme-store')).default;
const { shouldShowSnow } = await import('../snow');
useSpecialThemeStore.setState({ isEnabled: true });
expect(shouldShowSnow()).toBe(true);
useSpecialThemeStore.setState({ isEnabled: false });
expect(shouldShowSnow()).toBe(false);
useSpecialThemeStore.setState({ isEnabled: null });
vi.useFakeTimers();
vi.setSystemTime(new Date('2024-12-24T00:00:00Z'));
expect(shouldShowSnow()).toBe(true);
vi.setSystemTime(new Date('2024-07-04T00:00:00Z'));
expect(shouldShowSnow()).toBe(false);
});
});
+67
View File
@@ -0,0 +1,67 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
describe('update-favicon', () => {
beforeEach(() => {
document.head.innerHTML = '';
document.body.innerHTML = '';
vi.resetModules();
});
it('creates a favicon link and replaces it only when the target icon changes', async () => {
const { updateFavicon } = await import('../update-favicon');
updateFavicon(false);
expect(document.querySelectorAll('link[rel="icon"]')).toHaveLength(1);
expect(document.querySelector('link[rel="icon"]')?.getAttribute('href')).toBe('/favicon.ico');
updateFavicon(false);
expect(document.querySelectorAll('link[rel="icon"]')).toHaveLength(1);
updateFavicon(true);
expect(document.querySelectorAll('link[rel="icon"]')).toHaveLength(1);
expect(document.querySelector('link[rel="icon"]')?.getAttribute('href')).toBe('/favicon2.ico');
});
it('marks only non-special, non-routing aggregate sfw boards as sfw', async () => {
const { isSfwBoard } = await import('../update-favicon');
expect(
isSfwBoard({
pathname: '/',
isSpecialTheme: false,
isInAllView: false,
isInSubscriptionsView: false,
isInModView: false,
subplebbitAddress: 'music.eth',
directories: [{ address: 'music.eth', nsfw: false }],
}),
).toBe(false);
expect(
isSfwBoard({
pathname: '/music.eth',
isSpecialTheme: false,
isInAllView: false,
isInSubscriptionsView: false,
isInModView: false,
subplebbitAddress: 'music.eth',
directories: [
{ address: 'music.eth', nsfw: false },
{ address: 'flash.eth', nsfw: true },
],
}),
).toBe(true);
expect(
isSfwBoard({
pathname: '/flash.eth',
isSpecialTheme: false,
isInAllView: false,
isInSubscriptionsView: false,
isInModView: false,
subplebbitAddress: 'flash.eth',
directories: [{ address: 'flash.eth', nsfw: true }],
}),
).toBe(false);
});
});