import { render, cleanup } from '@testing-library/react'; import { describe, it, expect, vi, afterEach } from 'vitest'; import userEvent from '@testing-library/user-event'; import Calculator from './Calculator'; import '@testing-library/jest-dom/vitest'; afterEach(() => { cleanup(); }); describe('Calculator Component - digit inputs', () => { it('handles initial digit input replacing 0', async () => { const user = userEvent.setup(); const { container, getByRole } = render(); const display = container.querySelector('.text-2xl'); expect(display).toHaveTextContent('0'); const btn7 = getByRole('button', { name: '7' }); await user.click(btn7); expect(display).toHaveTextContent('7'); }); it('handles sequential digit inputs', async () => { const user = userEvent.setup(); const { container, getByRole } = render(); const display = container.querySelector('.text-2xl'); await user.click(getByRole('button', { name: '4' })); await user.click(getByRole('button', { name: '2' })); await user.click(getByRole('button', { name: '0' })); expect(display).toHaveTextContent('420'); }); it('handles waitingForOperand state (starts a new number after an operator)', async () => { const user = userEvent.setup(); const { container, getByRole } = render(); const display = container.querySelector('.text-2xl'); await user.click(getByRole('button', { name: '5' })); await user.click(getByRole('button', { name: '+' })); // The display still shows '5' while waiting for operand expect(display).toHaveTextContent('5'); // Entering a new digit replaces the display instead of appending await user.click(getByRole('button', { name: '3' })); expect(display).toHaveTextContent('3'); // Sequential digits append again await user.click(getByRole('button', { name: '1' })); expect(display).toHaveTextContent('31'); }); it('handles decimal inputs correctly', async () => { const user = userEvent.setup(); const { container, getByRole } = render(); const display = container.querySelector('.text-2xl'); await user.click(getByRole('button', { name: '2' })); await user.click(getByRole('button', { name: '.' })); await user.click(getByRole('button', { name: '5' })); expect(display).toHaveTextContent('2.5'); // Should not allow multiple decimals await user.click(getByRole('button', { name: '.' })); await user.click(getByRole('button', { name: '3' })); expect(display).toHaveTextContent('2.53'); }); it('evaluates parenthesized expressions with operator precedence', async () => { const user = userEvent.setup(); const { container, getByRole } = render(); const display = container.querySelector('.text-2xl'); await user.click(getByRole('button', { name: '(' })); await user.click(getByRole('button', { name: '2' })); await user.click(getByRole('button', { name: '+' })); await user.click(getByRole('button', { name: '3' })); await user.click(getByRole('button', { name: ')' })); await user.click(getByRole('button', { name: '×' })); await user.click(getByRole('button', { name: '4' })); await user.click(getByRole('button', { name: '=' })); expect(display).toHaveTextContent('20'); }); it('keeps keyboard focus trapped and closes on escape', async () => { const user = userEvent.setup(); const onClose = vi.fn(); const { getByRole } = render(); const dialog = getByRole('dialog', { name: 'Scientific Calculator' }); const buttons = dialog.querySelectorAll('button'); buttons[buttons.length - 1].focus(); await user.keyboard('{Tab}'); expect(buttons[0]).toHaveFocus(); await user.keyboard('{Escape}'); expect(onClose).toHaveBeenCalledTimes(1); }); });