mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-20 07:26:19 +00:00
tests: co-locate every Vitest suite next to its subject under src/
Eliminate the parallel `tests/` tree. Each `*.test.ts` / `*.test.tsx` now sits in the same directory as the module it covers, mirroring its filename (`Foo.ts` ↔ `Foo.test.ts`). The renderer-SDK mock used by component / hook tests moves to `src/__mocks__/nitro-renderer.ts` and the Vitest setup file becomes `src/test-setup.ts` — both still wired through `vitest.config.mts` exactly as before, only the paths changed. All 13 suites + 178/178 cases still pass. The production build is unaffected: rollup only follows imports from `src/index.tsx` and never crosses into `.test.ts` files, so test code is naturally tree-shaken out of the bundle. `yarn build` output is byte-for-byte the same on the user-facing chunks. tsconfig drops the now-redundant `tests` include entry. CLAUDE.md 'Layout convention' replaces the old `tests/` row with three rows documenting the new co-located convention, the `__mocks__/` directory and the `test-setup.ts` entry; ARCHITECTURE.md picks up the same update. The 'DO NOT CHANGE' qualifier on the layout is preserved — this rewrite IS the change, decided deliberately to make tests a first-class part of the source tree rather than a sibling project.
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
createEmptyMonitorSnapshot,
|
||||
formatMonitorHistoryOccurrence,
|
||||
formatMonitorLatestOccurrence,
|
||||
formatMonitorSource,
|
||||
formatVariableTimestamp,
|
||||
normalizeMonitorReason
|
||||
} from './WiredCreatorTools.helpers';
|
||||
|
||||
describe('WiredCreatorTools helpers', () =>
|
||||
{
|
||||
describe('createEmptyMonitorSnapshot', () =>
|
||||
{
|
||||
it('returns a zeroed-out snapshot with empty logs and history arrays', () =>
|
||||
{
|
||||
const snap = createEmptyMonitorSnapshot();
|
||||
|
||||
expect(snap.usageCurrentWindow).toBe(0);
|
||||
expect(snap.usageLimitPerWindow).toBe(0);
|
||||
expect(snap.isHeavy).toBe(false);
|
||||
expect(snap.killedRemainingSeconds).toBe(0);
|
||||
expect(snap.logs).toEqual([]);
|
||||
expect(snap.history).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns fresh arrays each call (no shared state)', () =>
|
||||
{
|
||||
const a = createEmptyMonitorSnapshot();
|
||||
const b = createEmptyMonitorSnapshot();
|
||||
|
||||
expect(a.logs).not.toBe(b.logs);
|
||||
expect(a.history).not.toBe(b.history);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatMonitorLatestOccurrence', () =>
|
||||
{
|
||||
const NOW = 1_700_000_000_000;
|
||||
|
||||
it('returns "/" when no occurrence has been recorded yet', () =>
|
||||
{
|
||||
expect(formatMonitorLatestOccurrence(0, NOW)).toBe('/');
|
||||
expect(formatMonitorLatestOccurrence(-1, NOW)).toBe('/');
|
||||
});
|
||||
|
||||
it('returns "Just now" for diffs under 5 seconds', () =>
|
||||
{
|
||||
const occurredAt = NOW / 1000;
|
||||
expect(formatMonitorLatestOccurrence(occurredAt, NOW)).toBe('Just now');
|
||||
});
|
||||
|
||||
it('returns "<n>s ago" for diffs under a minute', () =>
|
||||
{
|
||||
const tenSecondsAgo = (NOW - 10_000) / 1000;
|
||||
expect(formatMonitorLatestOccurrence(tenSecondsAgo, NOW)).toBe('10s ago');
|
||||
});
|
||||
|
||||
it('returns "<n>m ago" for diffs under an hour', () =>
|
||||
{
|
||||
const fiveMinutesAgo = (NOW - 5 * 60 * 1000) / 1000;
|
||||
expect(formatMonitorLatestOccurrence(fiveMinutesAgo, NOW)).toBe('5m ago');
|
||||
});
|
||||
|
||||
it('returns "<n>h ago" for diffs under a day', () =>
|
||||
{
|
||||
const threeHoursAgo = (NOW - 3 * 60 * 60 * 1000) / 1000;
|
||||
expect(formatMonitorLatestOccurrence(threeHoursAgo, NOW)).toBe('3h ago');
|
||||
});
|
||||
|
||||
it('returns "<n>d ago" for older diffs', () =>
|
||||
{
|
||||
const twoDaysAgo = (NOW - 2 * 24 * 60 * 60 * 1000) / 1000;
|
||||
expect(formatMonitorLatestOccurrence(twoDaysAgo, NOW)).toBe('2d ago');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatMonitorHistoryOccurrence', () =>
|
||||
{
|
||||
it('returns "/" for non-positive timestamps', () =>
|
||||
{
|
||||
expect(formatMonitorHistoryOccurrence(0)).toBe('/');
|
||||
expect(formatMonitorHistoryOccurrence(-5)).toBe('/');
|
||||
});
|
||||
|
||||
it('returns a non-empty formatted string for a real timestamp', () =>
|
||||
{
|
||||
const out = formatMonitorHistoryOccurrence(1_700_000_000);
|
||||
expect(out).not.toBe('/');
|
||||
expect(out.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatVariableTimestamp', () =>
|
||||
{
|
||||
it('returns "/" for zero, negative, or falsy values', () =>
|
||||
{
|
||||
expect(formatVariableTimestamp(0)).toBe('/');
|
||||
expect(formatVariableTimestamp(-1)).toBe('/');
|
||||
expect(formatVariableTimestamp(null)).toBe('/');
|
||||
});
|
||||
|
||||
it('formats a positive epoch-seconds value as a locale string', () =>
|
||||
{
|
||||
const out = formatVariableTimestamp(1_700_000_000);
|
||||
expect(out).not.toBe('/');
|
||||
expect(out.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatMonitorSource', () =>
|
||||
{
|
||||
it('falls back to "Room monitor" when both label and id are missing', () =>
|
||||
{
|
||||
expect(formatMonitorSource('', 0)).toBe('Room monitor');
|
||||
expect(formatMonitorSource('', -1)).toBe('Room monitor');
|
||||
});
|
||||
|
||||
it('returns just the label when there is no source id', () =>
|
||||
{
|
||||
expect(formatMonitorSource('wired-trigger', 0)).toBe('wired-trigger');
|
||||
});
|
||||
|
||||
it('appends "(#<id>)" when source id is positive', () =>
|
||||
{
|
||||
expect(formatMonitorSource('on-walk', 42)).toBe('on-walk (#42)');
|
||||
});
|
||||
|
||||
it('uses "wired" as default label when only the id is set', () =>
|
||||
{
|
||||
expect(formatMonitorSource('', 7)).toBe('wired (#7)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('normalizeMonitorReason', () =>
|
||||
{
|
||||
it('returns the trimmed reason when one is provided', () =>
|
||||
{
|
||||
expect(normalizeMonitorReason(' loop detected ')).toBe('loop detected');
|
||||
});
|
||||
|
||||
it('falls back to a placeholder when the reason is empty or whitespace', () =>
|
||||
{
|
||||
expect(normalizeMonitorReason('')).toContain('No detailed reason');
|
||||
expect(normalizeMonitorReason(' ')).toContain('No detailed reason');
|
||||
expect(normalizeMonitorReason(null)).toContain('No detailed reason');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,180 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { useWiredCreatorToolsUiStore } from './wiredCreatorToolsUiStore';
|
||||
|
||||
const INITIAL = {
|
||||
isVisible: false,
|
||||
activeTab: 'monitor' as const,
|
||||
inspectionType: 'furni' as const,
|
||||
variablesType: 'furni' as const,
|
||||
isMonitorHistoryOpen: false,
|
||||
isMonitorInfoOpen: false,
|
||||
isInspectionGiveOpen: false,
|
||||
isVariableManageOpen: false,
|
||||
isManagedGiveOpen: false,
|
||||
monitorHistorySeverityFilter: 'ALL' as const,
|
||||
monitorHistoryTypeFilter: 'ALL',
|
||||
variableManageTypeFilter: 'ALL',
|
||||
variableManageSort: 'highest_value',
|
||||
variableManagePage: 1
|
||||
};
|
||||
|
||||
describe('useWiredCreatorToolsUiStore', () =>
|
||||
{
|
||||
beforeEach(() =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.setState(INITIAL);
|
||||
});
|
||||
|
||||
it('exposes the documented defaults', () =>
|
||||
{
|
||||
const state = useWiredCreatorToolsUiStore.getState();
|
||||
|
||||
expect(state.isVisible).toBe(false);
|
||||
expect(state.activeTab).toBe('monitor');
|
||||
expect(state.inspectionType).toBe('furni');
|
||||
expect(state.variablesType).toBe('furni');
|
||||
expect(state.isMonitorHistoryOpen).toBe(false);
|
||||
expect(state.isMonitorInfoOpen).toBe(false);
|
||||
expect(state.isInspectionGiveOpen).toBe(false);
|
||||
expect(state.isVariableManageOpen).toBe(false);
|
||||
expect(state.isManagedGiveOpen).toBe(false);
|
||||
expect(state.monitorHistorySeverityFilter).toBe('ALL');
|
||||
expect(state.monitorHistoryTypeFilter).toBe('ALL');
|
||||
expect(state.variableManageTypeFilter).toBe('ALL');
|
||||
expect(state.variableManageSort).toBe('highest_value');
|
||||
expect(state.variableManagePage).toBe(1);
|
||||
});
|
||||
|
||||
describe('setIsVisible', () =>
|
||||
{
|
||||
it('accepts a direct boolean', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setIsVisible(true);
|
||||
expect(useWiredCreatorToolsUiStore.getState().isVisible).toBe(true);
|
||||
});
|
||||
|
||||
it('accepts a functional updater (toggle pattern)', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setIsVisible(prev => !prev);
|
||||
expect(useWiredCreatorToolsUiStore.getState().isVisible).toBe(true);
|
||||
|
||||
useWiredCreatorToolsUiStore.getState().setIsVisible(prev => !prev);
|
||||
expect(useWiredCreatorToolsUiStore.getState().isVisible).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setActiveTab', () =>
|
||||
{
|
||||
it('switches the active tab', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setActiveTab('variables');
|
||||
expect(useWiredCreatorToolsUiStore.getState().activeTab).toBe('variables');
|
||||
|
||||
useWiredCreatorToolsUiStore.getState().setActiveTab('inspection');
|
||||
expect(useWiredCreatorToolsUiStore.getState().activeTab).toBe('inspection');
|
||||
});
|
||||
});
|
||||
|
||||
describe('setInspectionType / setVariablesType', () =>
|
||||
{
|
||||
it('updates the inspection element type', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setInspectionType('user');
|
||||
expect(useWiredCreatorToolsUiStore.getState().inspectionType).toBe('user');
|
||||
});
|
||||
|
||||
it('updates the variables element type (including context)', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setVariablesType('context');
|
||||
expect(useWiredCreatorToolsUiStore.getState().variablesType).toBe('context');
|
||||
});
|
||||
});
|
||||
|
||||
describe('modal/popover flags', () =>
|
||||
{
|
||||
it('setIsMonitorHistoryOpen toggles the history modal flag', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setIsMonitorHistoryOpen(true);
|
||||
expect(useWiredCreatorToolsUiStore.getState().isMonitorHistoryOpen).toBe(true);
|
||||
|
||||
useWiredCreatorToolsUiStore.getState().setIsMonitorHistoryOpen(false);
|
||||
expect(useWiredCreatorToolsUiStore.getState().isMonitorHistoryOpen).toBe(false);
|
||||
});
|
||||
|
||||
it('setIsMonitorInfoOpen toggles the info modal flag', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setIsMonitorInfoOpen(true);
|
||||
expect(useWiredCreatorToolsUiStore.getState().isMonitorInfoOpen).toBe(true);
|
||||
});
|
||||
|
||||
it('setIsInspectionGiveOpen accepts a functional updater', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setIsInspectionGiveOpen(prev => !prev);
|
||||
expect(useWiredCreatorToolsUiStore.getState().isInspectionGiveOpen).toBe(true);
|
||||
|
||||
useWiredCreatorToolsUiStore.getState().setIsInspectionGiveOpen(prev => !prev);
|
||||
expect(useWiredCreatorToolsUiStore.getState().isInspectionGiveOpen).toBe(false);
|
||||
});
|
||||
|
||||
it('setIsVariableManageOpen takes a direct boolean', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setIsVariableManageOpen(true);
|
||||
expect(useWiredCreatorToolsUiStore.getState().isVariableManageOpen).toBe(true);
|
||||
});
|
||||
|
||||
it('setIsManagedGiveOpen accepts a functional updater', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setIsManagedGiveOpen(prev => !prev);
|
||||
expect(useWiredCreatorToolsUiStore.getState().isManagedGiveOpen).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('monitor history filters', () =>
|
||||
{
|
||||
it('setMonitorHistorySeverityFilter narrows to ERROR / WARNING / ALL', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setMonitorHistorySeverityFilter('ERROR');
|
||||
expect(useWiredCreatorToolsUiStore.getState().monitorHistorySeverityFilter).toBe('ERROR');
|
||||
|
||||
useWiredCreatorToolsUiStore.getState().setMonitorHistorySeverityFilter('WARNING');
|
||||
expect(useWiredCreatorToolsUiStore.getState().monitorHistorySeverityFilter).toBe('WARNING');
|
||||
|
||||
useWiredCreatorToolsUiStore.getState().setMonitorHistorySeverityFilter('ALL');
|
||||
expect(useWiredCreatorToolsUiStore.getState().monitorHistorySeverityFilter).toBe('ALL');
|
||||
});
|
||||
|
||||
it('setMonitorHistoryTypeFilter stores an arbitrary type label', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setMonitorHistoryTypeFilter('FurnitureRuntime');
|
||||
expect(useWiredCreatorToolsUiStore.getState().monitorHistoryTypeFilter).toBe('FurnitureRuntime');
|
||||
});
|
||||
});
|
||||
|
||||
describe('variable manage UI', () =>
|
||||
{
|
||||
it('setVariableManageTypeFilter / setVariableManageSort store string filters', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setVariableManageTypeFilter('Number');
|
||||
useWiredCreatorToolsUiStore.getState().setVariableManageSort('lowest_value');
|
||||
|
||||
expect(useWiredCreatorToolsUiStore.getState().variableManageTypeFilter).toBe('Number');
|
||||
expect(useWiredCreatorToolsUiStore.getState().variableManageSort).toBe('lowest_value');
|
||||
});
|
||||
|
||||
it('setVariableManagePage accepts a direct value', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setVariableManagePage(4);
|
||||
expect(useWiredCreatorToolsUiStore.getState().variableManagePage).toBe(4);
|
||||
});
|
||||
|
||||
it('setVariableManagePage accepts a functional updater (next/prev pagination)', () =>
|
||||
{
|
||||
useWiredCreatorToolsUiStore.getState().setVariableManagePage(2);
|
||||
useWiredCreatorToolsUiStore.getState().setVariableManagePage(prev => prev + 1);
|
||||
expect(useWiredCreatorToolsUiStore.getState().variableManagePage).toBe(3);
|
||||
|
||||
useWiredCreatorToolsUiStore.getState().setVariableManagePage(prev => Math.max(1, prev - 1));
|
||||
expect(useWiredCreatorToolsUiStore.getState().variableManagePage).toBe(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user