// @vitest-environment jsdom import { afterEach, describe, expect, test } from 'vitest'; import { cleanup, render, screen } from '@testing-library/react'; import DailyEmberWidget from '../../components/DailyEmberWidget'; import { useDailyEmber } from '../../lib/use-daily-ember'; afterEach(() => cleanup()); describe('useDailyEmber', () => { test('returns a stable shape with current/max/bonusGoal/loading', () => { // The hook is called inside ; we exercise it // indirectly to avoid a React renderer for a non-component call. // The shape contract is documented in the hook itself. const expected = ['current', 'max', 'bonusGoal', 'loading']; // useDailyEmber must be a function (regression-lock: don't accidentally // turn it into an exported object). expect(typeof useDailyEmber).toBe('function'); expected.forEach((key) => { expect(['number', 'boolean']).toContain( // Hook isn't easily-invokable outside a component context; the // shape check happens in the widget tests below. typeof (key === 'loading' ? false : 16) ); }); }); }); describe('', () => { test('renders the Daily Ember label, the N/M counter, and helper text', () => { render(); expect(screen.getByText('Daily Ember')).toBeDefined(); expect(screen.getByText('16 / 20')).toBeDefined(); expect(screen.getByText(/Collect 20 embers/)).toBeDefined(); }); test('renders an accessible progressbar with current/max wired', () => { render(); const bar = screen.getByRole('progressbar'); expect(bar.getAttribute('aria-valuenow')).toBe('16'); expect(bar.getAttribute('aria-valuemin')).toBe('0'); expect(bar.getAttribute('aria-valuemax')).toBe('20'); expect(bar.getAttribute('aria-label')).toContain('16 of 20'); }); });