← back to ClawCoder
src/tests/lib/profiles.test.ts
141 lines
import { describe, it, expect } from 'vitest'
import { PROFILES, getProfile, getProfilesByAge, getProfilesBySkill } from '@/lib/profiles/data'
import type { AgeGroup, SkillLevel } from '@/lib/profiles/types'
describe('Profile Data', () => {
it('has exactly 15 profiles', () => {
expect(PROFILES).toHaveLength(15)
})
it('covers all 5x3 matrix cells (5 age groups x 3 skill levels)', () => {
const ageGroups: AgeGroup[] = ['teen', 'young_adult', 'professional', 'pre_senior', 'senior']
const skillLevels: SkillLevel[] = ['beginner', 'intermediate', 'expert']
for (const age of ageGroups) {
for (const skill of skillLevels) {
const match = PROFILES.find(p => p.ageGroup === age && p.skillLevel === skill)
expect(match, `Missing profile for ${age}-${skill}`).toBeDefined()
}
}
})
it('has unique slugs for every profile', () => {
const slugs = PROFILES.map(p => p.slug)
const uniqueSlugs = new Set(slugs)
expect(uniqueSlugs.size).toBe(15)
})
it('has required fields on every profile', () => {
for (const profile of PROFILES) {
expect(profile.slug, `${profile.slug}: slug`).toBeTruthy()
expect(profile.ageGroup, `${profile.slug}: ageGroup`).toBeTruthy()
expect(profile.skillLevel, `${profile.slug}: skillLevel`).toBeTruthy()
expect(profile.displayName, `${profile.slug}: displayName`).toBeTruthy()
expect(profile.persona, `${profile.slug}: persona`).toBeTruthy()
expect(profile.ui, `${profile.slug}: ui`).toBeDefined()
expect(profile.template, `${profile.slug}: template`).toBeDefined()
expect(profile.template.content, `${profile.slug}: template.content`).toBeTruthy()
expect(Array.isArray(profile.skillBundle), `${profile.slug}: skillBundle`).toBe(true)
expect(Array.isArray(profile.mcpServers), `${profile.slug}: mcpServers`).toBe(true)
expect(profile.hooks, `${profile.slug}: hooks`).toBeDefined()
expect(Array.isArray(profile.subagents), `${profile.slug}: subagents`).toBe(true)
expect(Array.isArray(profile.learningPath), `${profile.slug}: learningPath`).toBe(true)
expect(Array.isArray(profile.rulesFiles), `${profile.slug}: rulesFiles`).toBe(true)
}
})
describe('Font size progression', () => {
it('seniors have larger fonts than teens at the same skill level', () => {
const skillLevels: SkillLevel[] = ['beginner', 'intermediate', 'expert']
for (const skill of skillLevels) {
const teen = PROFILES.find(p => p.ageGroup === 'teen' && p.skillLevel === skill)!
const senior = PROFILES.find(p => p.ageGroup === 'senior' && p.skillLevel === skill)!
expect(
senior.ui.fontSize,
`Senior ${skill} font (${senior.ui.fontSize}) should be > teen ${skill} font (${teen.ui.fontSize})`
).toBeGreaterThan(teen.ui.fontSize)
}
})
})
describe('MCP count progression', () => {
it('experts have more MCP servers than beginners within the same age group', () => {
const ageGroups: AgeGroup[] = ['teen', 'young_adult', 'professional', 'pre_senior', 'senior']
for (const age of ageGroups) {
const beginner = PROFILES.find(p => p.ageGroup === age && p.skillLevel === 'beginner')!
const expert = PROFILES.find(p => p.ageGroup === age && p.skillLevel === 'expert')!
expect(
expert.mcpServers.length,
`${age} expert MCPs (${expert.mcpServers.length}) should be >= ${age} beginner MCPs (${beginner.mcpServers.length})`
).toBeGreaterThanOrEqual(beginner.mcpServers.length)
}
})
})
describe('Touch target minimums', () => {
it('no profile has touch targets below 44px', () => {
for (const profile of PROFILES) {
expect(
profile.ui.touchTargetMin,
`${profile.slug} has touchTargetMin ${profile.ui.touchTargetMin} which is below 44`
).toBeGreaterThanOrEqual(44)
}
})
})
describe('Senior expert contrast', () => {
it('senior-expert has "high" contrast (not "enhanced")', () => {
const seniorExpert = getProfile('senior-expert')
expect(seniorExpert).toBeDefined()
expect(seniorExpert!.ui.contrast).toBe('high')
})
})
})
describe('getProfile', () => {
it('returns a profile by slug', () => {
const p = getProfile('teen-beginner')
expect(p).toBeDefined()
expect(p!.slug).toBe('teen-beginner')
expect(p!.ageGroup).toBe('teen')
expect(p!.skillLevel).toBe('beginner')
})
it('returns undefined for unknown slug', () => {
const p = getProfile('nonexistent-profile')
expect(p).toBeUndefined()
})
})
describe('getProfilesByAge', () => {
it('returns 3 profiles for each age group', () => {
const ageGroups: AgeGroup[] = ['teen', 'young_adult', 'professional', 'pre_senior', 'senior']
for (const age of ageGroups) {
const profiles = getProfilesByAge(age)
expect(profiles, `${age} should have 3 profiles`).toHaveLength(3)
for (const p of profiles) {
expect(p.ageGroup).toBe(age)
}
}
})
})
describe('getProfilesBySkill', () => {
it('returns 5 profiles for each skill level', () => {
const skillLevels: SkillLevel[] = ['beginner', 'intermediate', 'expert']
for (const skill of skillLevels) {
const profiles = getProfilesBySkill(skill)
expect(profiles, `${skill} should have 5 profiles`).toHaveLength(5)
for (const p of profiles) {
expect(p.skillLevel).toBe(skill)
}
}
})
})