From 862a907d02eb1907331dcdb2b0fdf91909ba24f6 Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 11 Mar 2026 10:12:53 +0800 Subject: [PATCH] chore: tests --- .../markdown-with-directive-schema.spec.ts | 73 +++++++++++++++++++ .../components/with-icon-card-item.spec.tsx | 29 ++++++++ .../components/with-icon-card-list.spec.tsx | 34 +++++++++ 3 files changed, 136 insertions(+) create mode 100644 web/app/components/base/markdown-with-directive/components/markdown-with-directive-schema.spec.ts create mode 100644 web/app/components/base/markdown-with-directive/components/with-icon-card-item.spec.tsx create mode 100644 web/app/components/base/markdown-with-directive/components/with-icon-card-list.spec.tsx diff --git a/web/app/components/base/markdown-with-directive/components/markdown-with-directive-schema.spec.ts b/web/app/components/base/markdown-with-directive/components/markdown-with-directive-schema.spec.ts new file mode 100644 index 0000000000..9e74ed43b4 --- /dev/null +++ b/web/app/components/base/markdown-with-directive/components/markdown-with-directive-schema.spec.ts @@ -0,0 +1,73 @@ +import { validateDirectiveProps } from './markdown-with-directive-schema' + +describe('markdown-with-directive-schema', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + // Validate the happy path for known directives. + describe('valid props', () => { + it('should return true for withiconcardlist when className is provided', () => { + expect(validateDirectiveProps('withiconcardlist', { className: 'custom-list' })).toBe(true) + }) + + it('should return true for withiconcarditem when icon is https URL', () => { + expect(validateDirectiveProps('withiconcarditem', { icon: 'https://example.com/icon.png' })).toBe(true) + }) + }) + + // Validate strict schema constraints and error branches. + describe('invalid props', () => { + it('should return false and log error for unknown directive name', () => { + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) + + const isValid = validateDirectiveProps('unknown-directive', { className: 'custom-list' }) + + expect(isValid).toBe(false) + expect(consoleErrorSpy).toHaveBeenCalledWith( + '[markdown-with-directive] Unknown directive name.', + expect.objectContaining({ + attributes: { className: 'custom-list' }, + directive: 'unknown-directive', + }), + ) + }) + + it('should return false and log error for non-http icon URL', () => { + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) + + const isValid = validateDirectiveProps('withiconcarditem', { icon: 'ftp://example.com/icon.png' }) + + expect(isValid).toBe(false) + expect(consoleErrorSpy).toHaveBeenCalledWith( + '[markdown-with-directive] Invalid directive props.', + expect.objectContaining({ + attributes: { icon: 'ftp://example.com/icon.png' }, + directive: 'withiconcarditem', + issues: expect.arrayContaining([ + expect.objectContaining({ + path: 'icon', + }), + ]), + }), + ) + }) + + it('should return false when extra field is provided to strict list schema', () => { + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) + + const isValid = validateDirectiveProps('withiconcardlist', { + className: 'custom-list', + extra: 'not-allowed', + }) + + expect(isValid).toBe(false) + expect(consoleErrorSpy).toHaveBeenCalledWith( + '[markdown-with-directive] Invalid directive props.', + expect.objectContaining({ + directive: 'withiconcardlist', + }), + ) + }) + }) +}) diff --git a/web/app/components/base/markdown-with-directive/components/with-icon-card-item.spec.tsx b/web/app/components/base/markdown-with-directive/components/with-icon-card-item.spec.tsx new file mode 100644 index 0000000000..e058947588 --- /dev/null +++ b/web/app/components/base/markdown-with-directive/components/with-icon-card-item.spec.tsx @@ -0,0 +1,29 @@ +import { render, screen } from '@testing-library/react' +import WithIconCardItem from './with-icon-card-item' + +vi.mock('next/image', () => ({ + default: ({ unoptimized: _unoptimized, ...props }: React.ImgHTMLAttributes & { unoptimized?: boolean }) => , +})) + +describe('WithIconCardItem', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + // Verify icon image and content rendering. + describe('rendering', () => { + it('should render icon image and children content', () => { + render( + + Card item content + , + ) + + const icon = screen.getByAltText('icon') + expect(icon).toBeInTheDocument() + expect(icon).toHaveAttribute('src', 'https://example.com/icon.png') + expect(icon).toHaveClass('object-contain') + expect(screen.getByText('Card item content')).toBeInTheDocument() + }) + }) +}) diff --git a/web/app/components/base/markdown-with-directive/components/with-icon-card-list.spec.tsx b/web/app/components/base/markdown-with-directive/components/with-icon-card-list.spec.tsx new file mode 100644 index 0000000000..d5b701b01c --- /dev/null +++ b/web/app/components/base/markdown-with-directive/components/with-icon-card-list.spec.tsx @@ -0,0 +1,34 @@ +import { render, screen } from '@testing-library/react' +import WithIconCardList from './with-icon-card-list' + +describe('WithIconCardList', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + // Verify baseline rendering and className merge behavior. + describe('rendering', () => { + it('should render children and merge custom className with base class', () => { + const { container } = render( + + List child + , + ) + + expect(screen.getByText('List child')).toBeInTheDocument() + expect(container.firstElementChild).toHaveClass('space-y-1') + expect(container.firstElementChild).toHaveClass('custom-list-class') + }) + + it('should keep base class when className is not provided', () => { + const { container } = render( + + Only base class + , + ) + + expect(screen.getByText('Only base class')).toBeInTheDocument() + expect(container.firstElementChild).toHaveClass('space-y-1') + }) + }) +})