Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

232 lines
8.4 KiB
TypeScript

import React from 'react';
import { describe, expect, test } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ButtonDemo } from '../src/demos/ButtonDemo';
import { InputDemo } from '../src/demos/InputDemo';
import { SelectDemo } from '../src/demos/SelectDemo';
import { CardDemo } from '../src/demos/CardDemo';
import { AlertDemo } from '../src/demos/AlertDemo';
import { TextDemo } from '../src/demos/TextDemo';
import { StackDemo } from '../src/demos/StackDemo';
import { BadgeDemo } from '../src/demos/BadgeDemo';
const findButton = (label: string) => screen.getByRole('button', { name: label });
describe('Button demo', () => {
test('primary variant uses alga primary background', () => {
render(<ButtonDemo />);
expect(findButton('Primary').style.background).toContain('var(--alga-primary)');
});
test('secondary variant uses alga secondary background', () => {
render(<ButtonDemo />);
expect(findButton('Secondary').style.background).toContain('var(--alga-secondary)');
});
test('destructive variant uses alga accent background', () => {
render(<ButtonDemo />);
expect(findButton('Destructive').style.background).toContain('var(--alga-accent)');
});
test('outline variant uses transparent background with border', () => {
render(<ButtonDemo />);
const btn = findButton('Outline');
expect(btn.style.background).toBe('transparent');
expect(btn.style.borderColor).toContain('var(--alga-border)');
});
test('ghost variant uses transparent background', () => {
render(<ButtonDemo />);
expect(findButton('Ghost').style.background).toBe('transparent');
});
test('link variant uses transparent background with underline', () => {
render(<ButtonDemo />);
const btn = findButton('Link');
expect(btn.style.background).toBe('transparent');
expect(btn.style.textDecoration).toBe('underline');
});
test('soft variant uses alga primary-soft background', () => {
render(<ButtonDemo />);
expect(findButton('Soft').style.background).toContain('var(--alga-primary-soft)');
});
test('dashed variant uses dashed border style', () => {
render(<ButtonDemo />);
const btn = findButton('Dashed');
expect(btn.style.background).toContain('var(--alga-primary-soft)');
expect(btn.style.borderStyle).toBe('dashed');
});
test('disabled buttons have reduced opacity and are non-interactive', () => {
render(<ButtonDemo />);
const buttons = screen.getAllByRole('button');
const disabledButtons = buttons.filter((b) => (b as HTMLButtonElement).disabled);
expect(disabledButtons.length).toBeGreaterThanOrEqual(1);
disabledButtons.forEach((btn) => {
expect((btn as HTMLElement).style.opacity).toBe('0.5');
});
});
});
describe('Input demo', () => {
test('input accepts text input', async () => {
const user = userEvent.setup();
render(<InputDemo />);
const input = screen.getByPlaceholderText('Enter a value') as HTMLInputElement;
await user.type(input, 'Hello');
expect(input.value).toBe('Hello');
});
test('placeholder text is visible when empty', () => {
render(<InputDemo />);
const input = screen.getByPlaceholderText('Enter a value') as HTMLInputElement;
expect(input.placeholder).toBe('Enter a value');
});
test('disabled input cannot receive focus', async () => {
const user = userEvent.setup();
render(<InputDemo />);
const input = screen.getByPlaceholderText('Disabled') as HTMLInputElement;
await user.click(input);
expect(input).not.toHaveFocus();
});
});
describe('CustomSelect demo', () => {
test('dropdown opens on click', async () => {
const user = userEvent.setup();
render(<SelectDemo />);
const trigger = screen.getAllByRole('combobox')[0];
await user.click(trigger);
expect(await screen.findByText('Paused')).toBeInTheDocument();
});
test('selected option is displayed', async () => {
const user = userEvent.setup();
render(<SelectDemo />);
const trigger = screen.getAllByRole('combobox')[0];
await user.click(trigger);
const paused = await screen.findByRole('option', { name: 'Paused' });
await user.click(paused);
expect(trigger).toHaveTextContent('Paused');
});
test('disabled select does not open', async () => {
const user = userEvent.setup();
render(<SelectDemo />);
const disabledTrigger = screen.getAllByRole('combobox')[1];
await user.click(disabledTrigger);
expect(screen.queryByText('Archived')).not.toBeInTheDocument();
});
});
describe('Card demo', () => {
test('card renders with border and padding', () => {
render(<CardDemo />);
const card = screen.getByText('Starter Plan').closest('div')?.parentElement as HTMLElement | null;
expect(card?.style.border).toContain('var(--alga-border)');
expect(card?.style.padding).toBe('16px');
});
test('card content is displayed inside', () => {
render(<CardDemo />);
expect(screen.getByText('Starter Plan')).toBeInTheDocument();
expect(screen.getByText('$24 / month')).toBeInTheDocument();
});
});
describe('Alert demo', () => {
test('info tone has left border and primary-soft background', () => {
render(<AlertDemo />);
const info = screen.getByText('Info').closest('[role="alert"]') as HTMLElement;
expect(info.style.background).toContain('var(--alga-primary-soft');
expect(info.style.borderLeft).toContain('var(--alga-primary');
});
test('success tone has green left border', () => {
render(<AlertDemo />);
const success = screen.getByText('Success').closest('[role="alert"]') as HTMLElement;
expect(success.style.borderLeft).toContain('var(--alga-success');
});
test('warning tone has amber/orange left border', () => {
render(<AlertDemo />);
const warning = screen.getByText('Warning').closest('[role="alert"]') as HTMLElement;
expect(warning.style.borderLeft).toContain('var(--alga-warning');
});
test('danger tone has red left border', () => {
render(<AlertDemo />);
const danger = screen.getByText('Danger').closest('[role="alert"]') as HTMLElement;
expect(danger.style.borderLeft).toContain('var(--alga-danger');
});
});
describe('Text demo', () => {
test('size props change font size', () => {
render(<TextDemo />);
const xs = screen.getByText('Extra small text');
const lg = screen.getByText('Large text');
expect(Number(xs.style.fontSize.replace('px', ''))).toBeLessThan(Number(lg.style.fontSize.replace('px', '')));
});
test('weight prop changes font weight', () => {
render(<TextDemo />);
const bold = screen.getByText('Bold 700');
expect(bold.style.fontWeight).toBe('700');
});
test('as prop renders correct HTML element', () => {
render(<TextDemo />);
expect(screen.getByText('Heading 1').tagName).toBe('H1');
expect(screen.getByText('Heading 2').tagName).toBe('H2');
expect(screen.getByText('Paragraph text rendered via Text').tagName).toBe('P');
expect(screen.getByText('Span inline text').tagName).toBe('SPAN');
});
});
describe('Stack demo', () => {
test('horizontal direction displays items in a row', () => {
render(<StackDemo />);
const row = screen.getByText('A').parentElement as HTMLElement;
expect(row.style.flexDirection).toBe('row');
});
test('vertical direction displays items in a column', () => {
render(<StackDemo />);
const column = screen.getByText('1').parentElement as HTMLElement;
expect(column.style.flexDirection).toBe('column');
});
test('gap prop adds spacing between items', () => {
render(<StackDemo />);
const row = screen.getByText('A').parentElement as HTMLElement;
expect(row.style.gap).toBe('8px');
});
});
describe('Badge demo', () => {
test('default tone renders neutral styling', () => {
render(<BadgeDemo />);
const badge = screen.getByText('Default');
expect(badge.style.background).toContain('var(--alga-muted');
});
test('success tone renders green styling', () => {
render(<BadgeDemo />);
const badge = screen.getByText('Success');
expect(badge.style.background).toContain('rgb(220, 252, 231)');
});
test('warning and danger tones render correctly', () => {
render(<BadgeDemo />);
const warning = screen.getByText('Warning');
const danger = screen.getByText('Danger');
expect(warning.style.background).toContain('rgb(254, 243, 199)');
expect(danger.style.background).toContain('rgb(255, 247, 237)');
});
});