import React from 'react'; import { describe, it, expect, vi } from 'vitest'; import { render, fireEvent } from '@testing-library/react'; import { PixelButton } from '../../actions'; describe('PixelButton — variant upgrades', () => { it('renders solid variant (default) with tone bg class', () => { const { getByRole } = render(Go); const btn = getByRole('button'); // solid uses tone.bg, which contains `bg-retro-green/18` expect(btn.className).toContain('bg-retro-green/18'); }); it('renders soft variant with tone.soft', () => { const { getByRole } = render( Go, ); const btn = getByRole('button'); expect(btn.className).toContain('bg-retro-green/8'); }); it('renders outline variant with transparent bg + border', () => { const { getByRole } = render( Go, ); const btn = getByRole('button'); expect(btn.className).toContain('bg-transparent'); expect(btn.className).toContain('border-retro-cyan/40'); }); it('renders ghost variant with transparent bg, no surface border', () => { const { getByRole } = render( Go, ); const btn = getByRole('button'); expect(btn.className).toContain('bg-transparent'); expect(btn.className).toContain('border-transparent'); }); }); describe('PixelButton — fullWidth', () => { it('applies w-full when fullWidth is true', () => { const { getByRole } = render(Go); expect(getByRole('button').className).toContain('w-full'); }); it('omits w-full when fullWidth is unset', () => { const { getByRole } = render(Go); expect(getByRole('button').className).not.toContain('w-full'); }); }); describe('PixelButton — loading min-width pin', () => { it('pins min-width on the inline style when flipping into loading', () => { // jsdom returns 0 width unless we stub getBoundingClientRect. const origGBCR = HTMLElement.prototype.getBoundingClientRect; HTMLElement.prototype.getBoundingClientRect = vi.fn(() => ({ x: 0, y: 0, width: 123, height: 32, top: 0, left: 0, right: 123, bottom: 32, toJSON: () => ({}), })); try { const { getByRole, rerender } = render(Save changes now); const btn = getByRole('button') as HTMLButtonElement; expect(btn.style.minWidth).toBe(''); rerender(Save changes now); expect(btn.style.minWidth).toBe('123px'); // When loading clears, the pin is released. rerender(Save changes now); expect(btn.style.minWidth).toBe(''); } finally { HTMLElement.prototype.getBoundingClientRect = origGBCR; } }); it('renders the spinner while loading and force-disables', () => { const { getByRole, getByTestId } = render( Save, ); expect(getByTestId('pxl-button-spinner')).toBeTruthy(); expect((getByRole('button') as HTMLButtonElement).disabled).toBe(true); }); }); describe('PixelButton — asChild', () => { it('clones a single child anchor, merges className + onClick + ref', () => { const childClick = vi.fn(); const wrapperClick = vi.fn(); const externalRef = React.createRef(); function Harness() { // forwardRef accepts the ref from forwardRef, but // because asChild clones the child, we attach the ref through the child // via the consumer (using mergeRefs equivalent inside the impl). return ( } > Go ); } const { getByTestId, container } = render(); const a = getByTestId('slot-link') as HTMLAnchorElement; // No button rendered — slot replaced root. expect(container.querySelector('button')).toBeNull(); // className merged: contains both base button styling and the custom class. expect(a.className).toContain('custom-class'); expect(a.className).toContain('inline-flex'); expect(a.className).toContain('border-retro-cyan/40'); // onClick runs both handlers. fireEvent.click(a); expect(childClick).toHaveBeenCalledTimes(1); expect(wrapperClick).toHaveBeenCalledTimes(1); // External ref attached to the slot anchor. expect(externalRef.current).toBe(a); }); it('falls back to