test(08-01): add failing tests for Calculator widget and registry constraints (RED)
- calculator-widget.test.tsx: 5 behaviour tests (render, arithmetic, div/0, keyboard, decimal) - widget-registry.test.tsx: DASH-11 structure check for all 8 widget types including new Phase-8 types - Both suites fail (RED baseline) — implementation does not exist yet
This commit is contained in:
@@ -0,0 +1,55 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { WIDGET_CONSTRAINTS, type WidgetType } from './widget-registry';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DASH-11: Every WidgetType entry in WIDGET_CONSTRAINTS must have
|
||||||
|
* the four numeric grid-constraint fields: minW, minH, defaultW, defaultH.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const ALL_WIDGET_TYPES: WidgetType[] = [
|
||||||
|
// Existing types
|
||||||
|
'clock',
|
||||||
|
'search',
|
||||||
|
'calendar',
|
||||||
|
'note',
|
||||||
|
// New types (Phase 8)
|
||||||
|
'calculator',
|
||||||
|
'favorites',
|
||||||
|
'link',
|
||||||
|
'stopwatch',
|
||||||
|
];
|
||||||
|
|
||||||
|
describe('WIDGET_CONSTRAINTS (DASH-11)', () => {
|
||||||
|
it('contains entries for all expected widget types', () => {
|
||||||
|
for (const type of ALL_WIDGET_TYPES) {
|
||||||
|
expect(
|
||||||
|
WIDGET_CONSTRAINTS,
|
||||||
|
`WIDGET_CONSTRAINTS must contain key "${type}"`,
|
||||||
|
).toHaveProperty(type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(ALL_WIDGET_TYPES)(
|
||||||
|
'WIDGET_CONSTRAINTS["%s"] has numeric minW, minH, defaultW, defaultH',
|
||||||
|
(type) => {
|
||||||
|
const constraints = WIDGET_CONSTRAINTS[type];
|
||||||
|
|
||||||
|
expect(typeof constraints.minW, `${type}.minW must be a number`).toBe('number');
|
||||||
|
expect(typeof constraints.minH, `${type}.minH must be a number`).toBe('number');
|
||||||
|
expect(typeof constraints.defaultW, `${type}.defaultW must be a number`).toBe('number');
|
||||||
|
expect(typeof constraints.defaultH, `${type}.defaultH must be a number`).toBe('number');
|
||||||
|
|
||||||
|
expect(constraints.minW, `${type}.minW must be >= 1`).toBeGreaterThanOrEqual(1);
|
||||||
|
expect(constraints.minH, `${type}.minH must be >= 1`).toBeGreaterThanOrEqual(1);
|
||||||
|
expect(constraints.defaultW, `${type}.defaultW >= minW`).toBeGreaterThanOrEqual(constraints.minW);
|
||||||
|
expect(constraints.defaultH, `${type}.defaultH >= minH`).toBeGreaterThanOrEqual(constraints.minH);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it('WIDGET_CONSTRAINTS contains new Phase-8 keys', () => {
|
||||||
|
expect(Object.keys(WIDGET_CONSTRAINTS)).toContain('calculator');
|
||||||
|
expect(Object.keys(WIDGET_CONSTRAINTS)).toContain('favorites');
|
||||||
|
expect(Object.keys(WIDGET_CONSTRAINTS)).toContain('link');
|
||||||
|
expect(Object.keys(WIDGET_CONSTRAINTS)).toContain('stopwatch');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { render, screen, fireEvent } from '@testing-library/react';
|
||||||
|
import { describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
// Mock next-intl — returns the key so tests are locale-agnostic
|
||||||
|
vi.mock('next-intl', () => ({
|
||||||
|
useTranslations: () => (key: string) => key,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Must import after mocks
|
||||||
|
import { CalculatorWidget } from './calculator-widget';
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
instanceId: 'calc-1',
|
||||||
|
config: {},
|
||||||
|
isEditMode: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('CalculatorWidget', () => {
|
||||||
|
it('renders with initial display "0"', () => {
|
||||||
|
render(<CalculatorWidget {...defaultProps} />);
|
||||||
|
const display = screen.getByLabelText('Anzeige');
|
||||||
|
expect(display).toHaveTextContent('0');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calculates 7 + 3 = 10', () => {
|
||||||
|
render(<CalculatorWidget {...defaultProps} />);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '7' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '+' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '3' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: /gleich/i }));
|
||||||
|
|
||||||
|
expect(screen.getByLabelText('Anzeige')).toHaveTextContent('10');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calculates 8 × 2 = 16', () => {
|
||||||
|
render(<CalculatorWidget {...defaultProps} />);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '8' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '×' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '2' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: /gleich/i }));
|
||||||
|
|
||||||
|
expect(screen.getByLabelText('Anzeige')).toHaveTextContent('16');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows "Fehler" on division by zero', () => {
|
||||||
|
render(<CalculatorWidget {...defaultProps} />);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '5' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '÷' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '0' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: /gleich/i }));
|
||||||
|
|
||||||
|
expect(screen.getByLabelText('Anzeige')).toHaveTextContent('Fehler');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles keyboard input: 5 * 6 Enter = 30', () => {
|
||||||
|
render(<CalculatorWidget {...defaultProps} />);
|
||||||
|
|
||||||
|
const container = screen.getByRole('application');
|
||||||
|
|
||||||
|
fireEvent.keyDown(container, { key: '5' });
|
||||||
|
fireEvent.keyDown(container, { key: '*' });
|
||||||
|
fireEvent.keyDown(container, { key: '6' });
|
||||||
|
fireEvent.keyDown(container, { key: 'Enter' });
|
||||||
|
|
||||||
|
expect(screen.getByLabelText('Anzeige')).toHaveTextContent('30');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles decimal comma: 1,5 + 1,5 = 3', () => {
|
||||||
|
render(<CalculatorWidget {...defaultProps} />);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '1' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: ',' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '5' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '+' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '1' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: ',' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '5' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: /gleich/i }));
|
||||||
|
|
||||||
|
expect(screen.getByLabelText('Anzeige')).toHaveTextContent('3');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user