test(01-01): add failing tests for palette module
- Tests cover all 7 CategoryType values with 3 shades each - Tests for headerGradient returning valid CSSProperties with linear-gradient - Tests for overviewHeaderGradient multi-stop gradient - Tests for amountColorClass: income, available, and expense paths
This commit is contained in:
137
frontend/src/lib/palette.test.ts
Normal file
137
frontend/src/lib/palette.test.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
palette,
|
||||
CategoryType,
|
||||
headerGradient,
|
||||
overviewHeaderGradient,
|
||||
amountColorClass,
|
||||
} from './palette'
|
||||
|
||||
describe('palette exports', () => {
|
||||
it('exports all 7 CategoryType values', () => {
|
||||
const expectedTypes: CategoryType[] = [
|
||||
'income',
|
||||
'bill',
|
||||
'variable_expense',
|
||||
'debt',
|
||||
'saving',
|
||||
'investment',
|
||||
'carryover',
|
||||
]
|
||||
expectedTypes.forEach((type) => {
|
||||
expect(palette[type]).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
it('has exactly 7 entries', () => {
|
||||
expect(Object.keys(palette)).toHaveLength(7)
|
||||
})
|
||||
|
||||
it('each category has light, medium, and base shades', () => {
|
||||
Object.entries(palette).forEach(([, shades]) => {
|
||||
expect(shades.light).toBeTruthy()
|
||||
expect(shades.medium).toBeTruthy()
|
||||
expect(shades.base).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
it('all shade values are non-empty oklch strings', () => {
|
||||
Object.entries(palette).forEach(([, shades]) => {
|
||||
expect(shades.light).toMatch(/oklch/)
|
||||
expect(shades.medium).toMatch(/oklch/)
|
||||
expect(shades.base).toMatch(/oklch/)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('headerGradient', () => {
|
||||
it('returns a CSSProperties object for a valid category type', () => {
|
||||
const result = headerGradient('bill')
|
||||
expect(result).toBeDefined()
|
||||
expect(typeof result).toBe('object')
|
||||
})
|
||||
|
||||
it('returns an object with a background property', () => {
|
||||
const result = headerGradient('income')
|
||||
expect(result).toHaveProperty('background')
|
||||
})
|
||||
|
||||
it('background value is a linear-gradient', () => {
|
||||
const result = headerGradient('bill')
|
||||
expect(String(result.background)).toMatch(/linear-gradient/)
|
||||
})
|
||||
|
||||
it('gradient uses the light and medium shades of the category', () => {
|
||||
const result = headerGradient('saving')
|
||||
expect(String(result.background)).toContain(palette.saving.light)
|
||||
expect(String(result.background)).toContain(palette.saving.medium)
|
||||
})
|
||||
})
|
||||
|
||||
describe('overviewHeaderGradient', () => {
|
||||
it('returns a CSSProperties object', () => {
|
||||
const result = overviewHeaderGradient()
|
||||
expect(result).toBeDefined()
|
||||
expect(typeof result).toBe('object')
|
||||
})
|
||||
|
||||
it('returns an object with a background property', () => {
|
||||
const result = overviewHeaderGradient()
|
||||
expect(result).toHaveProperty('background')
|
||||
})
|
||||
|
||||
it('background value is a multi-stop linear-gradient', () => {
|
||||
const result = overviewHeaderGradient()
|
||||
const bg = String(result.background)
|
||||
expect(bg).toMatch(/linear-gradient/)
|
||||
// Should have multiple color stops (at least 3 commas after the direction)
|
||||
const stops = bg.split('oklch').length - 1
|
||||
expect(stops).toBeGreaterThanOrEqual(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe('amountColorClass', () => {
|
||||
it('returns text-success for positive income (isIncome=true, actual > 0)', () => {
|
||||
expect(amountColorClass({ type: 'income', actual: 100, budgeted: 0, isIncome: true })).toBe(
|
||||
'text-success'
|
||||
)
|
||||
})
|
||||
|
||||
it('returns empty string for zero income (isIncome=true, actual = 0)', () => {
|
||||
expect(amountColorClass({ type: 'income', actual: 0, budgeted: 0, isIncome: true })).toBe('')
|
||||
})
|
||||
|
||||
it('returns text-warning when actual > budgeted (over-budget expense)', () => {
|
||||
expect(amountColorClass({ type: 'bill', actual: 200, budgeted: 100 })).toBe('text-warning')
|
||||
})
|
||||
|
||||
it('returns empty string when actual equals budgeted (exactly on budget)', () => {
|
||||
expect(amountColorClass({ type: 'bill', actual: 100, budgeted: 100 })).toBe('')
|
||||
})
|
||||
|
||||
it('returns empty string when actual < budgeted (under budget)', () => {
|
||||
expect(amountColorClass({ type: 'bill', actual: 50, budgeted: 100 })).toBe('')
|
||||
})
|
||||
|
||||
it('returns empty string when isAvailable=true and actual is zero', () => {
|
||||
expect(amountColorClass({ type: 'bill', actual: 0, budgeted: 0, isAvailable: true })).toBe('')
|
||||
})
|
||||
|
||||
it('returns text-success when isAvailable=true and actual > 0', () => {
|
||||
expect(
|
||||
amountColorClass({ type: 'bill', actual: 500, budgeted: 0, isAvailable: true })
|
||||
).toBe('text-success')
|
||||
})
|
||||
|
||||
it('returns text-destructive when isAvailable=true and actual < 0', () => {
|
||||
expect(
|
||||
amountColorClass({ type: 'bill', actual: -100, budgeted: 0, isAvailable: true })
|
||||
).toBe('text-destructive')
|
||||
})
|
||||
|
||||
it('returns text-destructive for negative income (isIncome=true, actual < 0)', () => {
|
||||
expect(
|
||||
amountColorClass({ type: 'income', actual: -50, budgeted: 0, isIncome: true })
|
||||
).toBe('text-destructive')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user