import React from 'react';
import { describe, it, expect } from 'vitest';
import { render } from '@testing-library/react';
import { PixelAvatar } from '../../data/PixelAvatar';
describe('PixelAvatar — status dot', () => {
it('does not render a status dot when status is unset', () => {
const { container } = render();
// Status dot uses [data-status]; no role=status (avoid live-region spam).
expect(container.querySelector('[data-status]')).toBeNull();
});
it.each([
['online', 'bg-retro-green'],
['away', 'bg-retro-gold'],
['busy', 'bg-retro-red'],
['offline', 'bg-retro-muted'],
] as const)('renders the %s status dot with the matching fill class', (status, fill) => {
const { container } = render();
const dot = container.querySelector('[data-status]') as HTMLElement | null;
expect(dot).not.toBeNull();
expect(dot!.dataset.status).toBe(status);
expect(dot!.className).toContain(fill);
// Status text is composed into the avatar's accessible name on the frame.
const frame = container.querySelector('[data-shape]') as HTMLElement;
expect(frame.getAttribute('aria-label')).toBe(`Jane Doe (${status})`);
});
});
describe('PixelAvatar — colorSeed determinism', () => {
function getToneClass(name: string, colorSeed: string): string {
const { container } = render();
const frame = container.querySelector('[title]') as HTMLElement;
return frame.className;
}
it('produces identical tone classes for the same colorSeed across renders', () => {
const a = getToneClass('Joangel', 'joangel@example.com');
const b = getToneClass('Otro', 'joangel@example.com');
expect(a).toBe(b);
});
it('produces stable tone classes across many calls (no randomness)', () => {
const samples = Array.from({ length: 5 }, () => getToneClass('Same', 'seed-fixed-42'));
const unique = new Set(samples);
expect(unique.size).toBe(1);
});
it('explicit tone overrides colorSeed', () => {
const seeded = getToneClass('Person', 'whatever-seed');
const { container } = render();
const frame = container.querySelector('[title]') as HTMLElement;
// The explicit purple tone should win, so the class set must include purple border tokens
// which differ from the seed-derived tone for "whatever-seed". We assert override by
// verifying the purple tone class set is applied.
expect(frame.className).toContain('purple');
// And the seeded version should NOT have used the override path.
expect(seeded).not.toBe(frame.className);
});
it('different colorSeeds eventually land on different tones (palette of 6)', () => {
const seeds = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'];
const classes = new Set(seeds.map((seed) => getToneClass('Test', seed)));
expect(classes.size).toBeGreaterThan(1);
});
});
describe('PixelAvatar — lazy image attrs', () => {
it('adds loading="lazy" and decoding="async" to the img element', () => {
const { container } = render(
,
);
const img = container.querySelector('img') as HTMLImageElement | null;
expect(img).not.toBeNull();
expect(img!.getAttribute('loading')).toBe('lazy');
expect(img!.getAttribute('decoding')).toBe('async');
expect(img!.getAttribute('alt')).toBe('Jane Doe');
});
it('falls back to initials when src is missing (no img tag)', () => {
const { container } = render();
expect(container.querySelector('img')).toBeNull();
const frame = container.querySelector('[title="Jane Doe"]') as HTMLElement;
expect(frame.textContent).toBe('JD');
});
});
describe('PixelAvatar — sizes & shape', () => {
it.each([
['xs', 'h-6'],
['sm', 'h-8'],
['md', 'h-10'],
['lg', 'h-12'],
['xl', 'h-16'],
] as const)('size %s applies the matching dimension class', (size, dimClass) => {
const { container } = render();
const frame = container.querySelector('[title="JD"]') as HTMLElement;
expect(frame.className).toContain(dimClass);
});
it('defaults shape to circle (rounded-full on linear surface)', () => {
const { container } = render();
const frame = container.querySelector('[title="JD"]') as HTMLElement;
expect(frame.dataset.shape).toBe('circle');
expect(frame.className).toContain('rounded-full');
});
it('shape=square applies rounded-none', () => {
const { container } = render();
const frame = container.querySelector('[title="JD"]') as HTMLElement;
expect(frame.dataset.shape).toBe('square');
expect(frame.className).toContain('rounded-none');
});
it('shape=rounded applies rounded-lg on linear surface', () => {
const { container } = render();
const frame = container.querySelector('[title="JD"]') as HTMLElement;
expect(frame.dataset.shape).toBe('rounded');
expect(frame.className).toContain('rounded-lg');
});
});