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,96 @@
|
||||
/* @vitest-environment jsdom */
|
||||
|
||||
import { NitroLogger } from '@nitrots/nitro-renderer';
|
||||
import { cleanup, render, screen } from '@testing-library/react';
|
||||
import { FC } from 'react';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { WidgetErrorBoundary } from './WidgetErrorBoundary';
|
||||
|
||||
// `import { NitroLogger } from '@nitrots/nitro-renderer'` resolves to
|
||||
// `src/__mocks__/nitro-renderer.ts` via the alias in vitest.config.mts.
|
||||
// The SUT imports the same path, so both reach the same vi.fn instance.
|
||||
|
||||
describe('WidgetErrorBoundary', () =>
|
||||
{
|
||||
beforeEach(() =>
|
||||
{
|
||||
vi.clearAllMocks();
|
||||
// react-error-boundary lets React's "uncaught error" log through
|
||||
// by default — silence it so jsdom doesn't dump a stack trace
|
||||
// every time we deliberately throw below.
|
||||
vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() =>
|
||||
{
|
||||
cleanup();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('renders its children when nothing throws', () =>
|
||||
{
|
||||
render(
|
||||
<WidgetErrorBoundary name="HappyPath">
|
||||
<span data-testid="child">visible</span>
|
||||
</WidgetErrorBoundary>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('child')).toHaveTextContent('visible');
|
||||
});
|
||||
|
||||
it('swallows a render-time error to a silent fallback and logs it', () =>
|
||||
{
|
||||
const Boom: FC = () =>
|
||||
{
|
||||
throw new Error('kaboom');
|
||||
};
|
||||
|
||||
const { container } = render(
|
||||
<WidgetErrorBoundary name="Boom">
|
||||
<Boom />
|
||||
</WidgetErrorBoundary>
|
||||
);
|
||||
|
||||
// Default fallback is `() => null` → boundary subtree is empty.
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
|
||||
expect(NitroLogger.error).toHaveBeenCalledTimes(1);
|
||||
const [ message, err ] = (NitroLogger.error as ReturnType<typeof vi.fn>).mock.calls[0];
|
||||
expect(message).toBe('[Widget:Boom] crashed');
|
||||
expect(err).toBeInstanceOf(Error);
|
||||
expect((err as Error).message).toBe('kaboom');
|
||||
});
|
||||
|
||||
it('renders a custom fallback node when provided', () =>
|
||||
{
|
||||
const Boom: FC = () =>
|
||||
{
|
||||
throw new Error('explode');
|
||||
};
|
||||
|
||||
render(
|
||||
<WidgetErrorBoundary name="WithFallback" fallback={ <div data-testid="fb">offline</div> }>
|
||||
<Boom />
|
||||
</WidgetErrorBoundary>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('fb')).toHaveTextContent('offline');
|
||||
});
|
||||
|
||||
it('uses "unknown" as the widget name when the prop is omitted', () =>
|
||||
{
|
||||
const Boom: FC = () =>
|
||||
{
|
||||
throw new Error('anonymous');
|
||||
};
|
||||
|
||||
render(
|
||||
<WidgetErrorBoundary>
|
||||
<Boom />
|
||||
</WidgetErrorBoundary>
|
||||
);
|
||||
|
||||
expect(NitroLogger.error).toHaveBeenCalledTimes(1);
|
||||
expect((NitroLogger.error as ReturnType<typeof vi.fn>).mock.calls[0][0]).toBe('[Widget:unknown] crashed');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user