| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- import { describe, expect, test } from 'vitest'
- import { resolveSteps } from './connect.resolver'
- import { connectSchema, EXTRA_PACKAGES, INSTALL_COMMANDS } from './connect.schema'
- import type { ConnectState } from './Connect.types'
- // ============================================================================
- // Schema Structure Tests
- // ============================================================================
- describe('connect.schema:structure', () => {
- test('should have all required modes', () => {
- const modeIds = connectSchema.modes.map((m) => m.id)
- expect(modeIds).toContain('framework')
- expect(modeIds).toContain('direct')
- expect(modeIds).toContain('orm')
- expect(modeIds).toContain('mcp')
- })
- test('each mode should have required properties', () => {
- connectSchema.modes.forEach((mode) => {
- expect(mode.id).toBeDefined()
- expect(mode.label).toBeDefined()
- expect(mode.description).toBeDefined()
- expect(mode.fields).toBeDefined()
- expect(Array.isArray(mode.fields)).toBe(true)
- })
- })
- test('framework mode should have correct fields', () => {
- const frameworkMode = connectSchema.modes.find((m) => m.id === 'framework')
- expect(frameworkMode?.fields).toContain('framework')
- expect(frameworkMode?.fields).toContain('frameworkVariant')
- expect(frameworkMode?.fields).toContain('library')
- expect(frameworkMode?.fields).toContain('frameworkUi')
- })
- test('direct mode should have correct fields', () => {
- const directMode = connectSchema.modes.find((m) => m.id === 'direct')
- expect(directMode?.fields).toContain('connectionMethod')
- expect(directMode?.fields).toContain('useSharedPooler')
- expect(directMode?.fields).toContain('connectionType')
- })
- test('orm mode should have correct fields', () => {
- const ormMode = connectSchema.modes.find((m) => m.id === 'orm')
- expect(ormMode?.fields).toContain('orm')
- })
- test('mcp mode should have correct fields', () => {
- const mcpMode = connectSchema.modes.find((m) => m.id === 'mcp')
- expect(mcpMode?.fields).toContain('mcpClient')
- expect(mcpMode?.fields).toContain('mcpReadonly')
- expect(mcpMode?.fields).toContain('mcpFeatures')
- })
- test('all mode fields should exist in fields definition', () => {
- connectSchema.modes.forEach((mode) => {
- mode.fields.forEach((fieldId) => {
- expect(
- connectSchema.fields[fieldId],
- `Field "${fieldId}" in mode "${mode.id}" should exist in fields definition`
- ).toBeDefined()
- })
- })
- })
- })
- // ============================================================================
- // Field Definition Tests
- // ============================================================================
- describe('connect.schema:fields', () => {
- test('framework field should have correct type', () => {
- const field = connectSchema.fields.framework
- expect(field.type).toBe('select')
- expect(field.options).toEqual({ source: 'frameworks' })
- expect(field.defaultValue).toBe('nextjs')
- })
- test('frameworkVariant field should depend on framework', () => {
- const field = connectSchema.fields.frameworkVariant
- expect(field.dependsOn).toEqual({ framework: ['nextjs', 'react'] })
- })
- test('frameworkUi field should be a switch type', () => {
- const field = connectSchema.fields.frameworkUi
- expect(field.type).toBe('switch')
- expect(field.defaultValue).toBe(false)
- expect(field.dependsOn).toEqual({ framework: ['nextjs', 'react'] })
- })
- test('connectionMethod field should have radio-list type', () => {
- const field = connectSchema.fields.connectionMethod
- expect(field.type).toBe('radio-list')
- expect(field.options).toEqual({ source: 'connectionMethods' })
- expect(field.defaultValue).toBe('direct')
- })
- test('useSharedPooler field should depend on transaction connection method', () => {
- const field = connectSchema.fields.useSharedPooler
- expect(field.type).toBe('switch')
- expect(field.dependsOn).toEqual({ connectionMethod: ['transaction'] })
- })
- test('orm field should have radio-list type', () => {
- const field = connectSchema.fields.orm
- expect(field.type).toBe('radio-list')
- expect(field.options).toEqual({ source: 'orms' })
- expect(field.defaultValue).toBe('prisma')
- })
- test('mcpClient field should have select type', () => {
- const field = connectSchema.fields.mcpClient
- expect(field.type).toBe('select')
- expect(field.options).toEqual({ source: 'mcpClients' })
- expect(field.defaultValue).toBe('claude-code')
- })
- test('mcpFeatures field should have multi-select type', () => {
- const field = connectSchema.fields.mcpFeatures
- expect(field.type).toBe('multi-select')
- expect(field.options).toEqual({ source: 'mcpFeatures' })
- })
- })
- // ============================================================================
- // Install Commands Tests
- // ============================================================================
- describe('connect.schema:INSTALL_COMMANDS', () => {
- test('should have install command for briven-js', () => {
- expect(INSTALL_COMMANDS.brivenjs).toBe('npm install @supabase/supabase-js')
- })
- test('should have install command for briven-py', () => {
- expect(INSTALL_COMMANDS.brivenpy).toBe('pip install briven')
- })
- test('should have install command for briven-flutter', () => {
- expect(INSTALL_COMMANDS.brivenflutter).toBe('flutter pub add briven_flutter')
- })
- test('should have install command for briven-swift', () => {
- expect(INSTALL_COMMANDS.brivenswift).toContain('swift package add-dependency')
- })
- test('should have install command for briven-kt', () => {
- expect(INSTALL_COMMANDS.brivenkt).toContain('io.github.jan-tennert.briven')
- })
- })
- describe('connect.schema:EXTRA_PACKAGES', () => {
- test('should have @supabase/ssr as extra package for nextjs app router with brivenjs', () => {
- expect(EXTRA_PACKAGES.brivenjs?.['nextjs/app']).toContain('@supabase/ssr')
- })
- test('should not have extra packages for nextjs pages router', () => {
- expect(EXTRA_PACKAGES.brivenjs?.['nextjs/pages']).toBeUndefined()
- })
- test('should have @supabase/ssr as extra package for remix with brivenjs', () => {
- expect(EXTRA_PACKAGES.brivenjs?.remix).toContain('@supabase/ssr')
- })
- })
- // ============================================================================
- // Steps Resolution Integration Tests
- // ============================================================================
- describe('connect.schema:steps resolution', () => {
- describe('framework mode steps', () => {
- test('should resolve steps for nextjs without shadcn', () => {
- const state: ConnectState = { mode: 'framework', framework: 'nextjs', frameworkUi: false }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.length).toBeGreaterThan(0)
- expect(steps.find((s) => s.id === 'install')).toBeDefined()
- expect(steps.find((s) => s.id === 'install-skills')).toBeDefined()
- })
- test('should use "Install packages" title for nextjs app router', () => {
- const state: ConnectState = {
- mode: 'framework',
- framework: 'nextjs',
- frameworkVariant: 'app',
- frameworkUi: false,
- }
- const steps = resolveSteps(connectSchema, state)
- const installStep = steps.find((s) => s.id === 'install')
- expect(installStep?.title).toBe('Install packages')
- })
- test('should use "Install package" title for nextjs pages router', () => {
- const state: ConnectState = {
- mode: 'framework',
- framework: 'nextjs',
- frameworkVariant: 'pages',
- frameworkUi: false,
- }
- const steps = resolveSteps(connectSchema, state)
- const installStep = steps.find((s) => s.id === 'install')
- expect(installStep?.title).toBe('Install package')
- })
- test('should use "Install packages" title for remix install step', () => {
- const state: ConnectState = { mode: 'framework', framework: 'remix' }
- const steps = resolveSteps(connectSchema, state)
- const installStep = steps.find((s) => s.id === 'install')
- expect(installStep?.title).toBe('Install packages')
- })
- test('should use "Install package" title for frameworks without extra packages', () => {
- const state: ConnectState = { mode: 'framework', framework: 'vuejs' }
- const steps = resolveSteps(connectSchema, state)
- const installStep = steps.find((s) => s.id === 'install')
- expect(installStep?.title).toBe('Install package')
- })
- test('should resolve shadcn steps for nextjs with frameworkUi true', () => {
- const state: ConnectState = { mode: 'framework', framework: 'nextjs', frameworkUi: true }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'shadcn-add')).toBeDefined()
- expect(steps.find((s) => s.id === 'shadcn-env')).toBeDefined()
- expect(steps.find((s) => s.id === 'shadcn-explore')).toBeDefined()
- })
- test('should resolve steps for react without shadcn', () => {
- const state: ConnectState = { mode: 'framework', framework: 'react', frameworkUi: false }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.length).toBeGreaterThan(0)
- expect(steps.find((s) => s.id === 'install')).toBeDefined()
- })
- test('should resolve shadcn steps for react with frameworkUi true', () => {
- const state: ConnectState = { mode: 'framework', framework: 'react', frameworkUi: true }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'shadcn-add')).toBeDefined()
- expect(steps.find((s) => s.id === 'shadcn-env')).toBeDefined()
- })
- test('should resolve default steps for other frameworks', () => {
- const state: ConnectState = { mode: 'framework', framework: 'remix' }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.length).toBeGreaterThan(0)
- expect(steps.find((s) => s.id === 'install')).toBeDefined()
- expect(steps.find((s) => s.id === 'configure')).toBeDefined()
- })
- })
- describe('direct mode steps', () => {
- test('should resolve connection step for default direct mode', () => {
- const state: ConnectState = { mode: 'direct' }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'connection')).toBeDefined()
- })
- test('should resolve install and files steps for nodejs connection type', () => {
- const state: ConnectState = { mode: 'direct', connectionType: 'nodejs' }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'direct-install')).toBeDefined()
- expect(steps.find((s) => s.id === 'direct-files')).toBeDefined()
- })
- test('should resolve install and files steps for golang connection type', () => {
- const state: ConnectState = { mode: 'direct', connectionType: 'golang' }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'direct-install')).toBeDefined()
- expect(steps.find((s) => s.id === 'direct-files')).toBeDefined()
- })
- test('should resolve install and files steps for python connection type', () => {
- const state: ConnectState = { mode: 'direct', connectionType: 'python' }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'direct-install')).toBeDefined()
- })
- test('should resolve install and files steps for dotnet connection type', () => {
- const state: ConnectState = { mode: 'direct', connectionType: 'dotnet' }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'direct-install')).toBeDefined()
- })
- })
- describe('orm mode steps', () => {
- test('should resolve install and configure steps for prisma', () => {
- const state: ConnectState = { mode: 'orm', orm: 'prisma' }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'install')).toBeDefined()
- expect(steps.find((s) => s.id === 'configure')).toBeDefined()
- expect(steps.find((s) => s.id === 'install-skills')).toBeDefined()
- })
- test('should resolve install and configure steps for drizzle', () => {
- const state: ConnectState = { mode: 'orm', orm: 'drizzle' }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'install')).toBeDefined()
- expect(steps.find((s) => s.id === 'configure')).toBeDefined()
- })
- })
- describe('mcp mode steps', () => {
- test('should resolve configure step for cursor client', () => {
- const state: ConnectState = { mode: 'mcp', mcpClient: 'cursor' }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'configure-mcp')).toBeDefined()
- expect(steps.find((s) => s.id === 'install-skills')).toBeDefined()
- })
- test('should resolve codex-specific steps for codex client', () => {
- const state: ConnectState = { mode: 'mcp', mcpClient: 'codex' }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'codex-add-server')).toBeDefined()
- expect(steps.find((s) => s.id === 'codex-enable-remote')).toBeDefined()
- expect(steps.find((s) => s.id === 'codex-authenticate')).toBeDefined()
- expect(steps.find((s) => s.id === 'codex-verify')).toBeDefined()
- })
- test('should resolve claude-code-specific steps for claude-code client', () => {
- const state: ConnectState = { mode: 'mcp', mcpClient: 'claude-code' }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'claude-add-server')).toBeDefined()
- expect(steps.find((s) => s.id === 'claude-authenticate')).toBeDefined()
- })
- test('should resolve default mcp steps for other clients', () => {
- const state: ConnectState = { mode: 'mcp', mcpClient: 'unknown-client' }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'configure-mcp')).toBeDefined()
- })
- })
- describe('skills install step', () => {
- test('should include skills install step in all modes', () => {
- const modes: ConnectState['mode'][] = ['framework', 'direct', 'orm', 'mcp']
- modes.forEach((mode) => {
- const state: ConnectState = { mode }
- const steps = resolveSteps(connectSchema, state)
- expect(
- steps.find((s) => s.id === 'install-skills'),
- `Mode "${mode}" should have skills install step`
- ).toBeDefined()
- })
- })
- })
- })
- // ============================================================================
- // Step Content Path Tests
- // ============================================================================
- describe('connect.schema:step content paths', () => {
- test('install step should have valid content path', () => {
- const state: ConnectState = { mode: 'framework', framework: 'nextjs' }
- const steps = resolveSteps(connectSchema, state)
- const installStep = steps.find((s) => s.id === 'install')
- expect(installStep?.content).toBe('steps/install')
- })
- test('shadcn command step should have valid content path', () => {
- const state: ConnectState = { mode: 'framework', framework: 'nextjs', frameworkUi: true }
- const steps = resolveSteps(connectSchema, state)
- const shadcnStep = steps.find((s) => s.id === 'shadcn-add')
- expect(shadcnStep?.content).toBe('steps/shadcn/command')
- })
- test('shadcn explore step should have valid content path', () => {
- const state: ConnectState = { mode: 'framework', framework: 'nextjs', frameworkUi: true }
- const steps = resolveSteps(connectSchema, state)
- const exploreStep = steps.find((s) => s.id === 'shadcn-explore')
- expect(exploreStep?.content).toBe('steps/shadcn/explore')
- })
- test('shadcn env step should have valid content path', () => {
- const state: ConnectState = { mode: 'framework', framework: 'nextjs', frameworkUi: true }
- const steps = resolveSteps(connectSchema, state)
- const envStep = steps.find((s) => s.id === 'shadcn-env')
- expect(envStep?.content).toBe('steps/shadcn/env')
- })
- test('direct connection step should have valid content path', () => {
- const state: ConnectState = { mode: 'direct' }
- const steps = resolveSteps(connectSchema, state)
- const connectionStep = steps.find((s) => s.id === 'connection')
- expect(connectionStep?.content).toBe('steps/direct-connection')
- })
- test('skills install step should have valid content path', () => {
- const state: ConnectState = { mode: 'framework' }
- const steps = resolveSteps(connectSchema, state)
- const skillsStep = steps.find((s) => s.id === 'install-skills')
- expect(skillsStep?.content).toBe('steps/skills-install')
- })
- test('orm configure step should use template content path', () => {
- // The ORM configure step uses a template {{orm}} that gets resolved
- // by the dynamic import system, not the resolver
- const state: ConnectState = { mode: 'orm', orm: 'prisma' }
- const steps = resolveSteps(connectSchema, state)
- const configureStep = steps.find((s) => s.id === 'configure')
- // The content path uses template syntax for the component loader
- expect(configureStep?.content).toBe('{{orm}}')
- })
- test('mcp cursor configure step should have valid content path', () => {
- const state: ConnectState = { mode: 'mcp', mcpClient: 'cursor' }
- const steps = resolveSteps(connectSchema, state)
- const configureStep = steps.find((s) => s.id === 'configure-mcp')
- expect(configureStep?.content).toBe('steps/mcp/cursor')
- })
- test('codex steps should have valid content paths', () => {
- const state: ConnectState = { mode: 'mcp', mcpClient: 'codex' }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'codex-add-server')?.content).toBe(
- 'steps/mcp/codex/add-server'
- )
- expect(steps.find((s) => s.id === 'codex-enable-remote')?.content).toBe(
- 'steps/mcp/codex/enable-remote'
- )
- expect(steps.find((s) => s.id === 'codex-authenticate')?.content).toBe(
- 'steps/mcp/codex/authenticate'
- )
- expect(steps.find((s) => s.id === 'codex-verify')?.content).toBe('steps/mcp/codex/verify')
- })
- test('claude-code steps should have valid content paths', () => {
- const state: ConnectState = { mode: 'mcp', mcpClient: 'claude-code' }
- const steps = resolveSteps(connectSchema, state)
- expect(steps.find((s) => s.id === 'claude-add-server')?.content).toBe(
- 'steps/mcp/claude-code/add-server'
- )
- expect(steps.find((s) => s.id === 'claude-authenticate')?.content).toBe(
- 'steps/mcp/claude-code/authenticate'
- )
- })
- })
|