ConusAI vendor 81941888f
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import { isValidElement, type ReactElement } from 'react'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { ExportNotesButton } from '@/features/export-notes'
|
||||
import { UploadsLeft } from '@/features/rate-limit'
|
||||
import { SearchNotes } from '@/features/search-notes'
|
||||
import { panelSlots } from '@/widgets/module-panel/model/slots'
|
||||
import type { ModuleName } from '@/widgets/module-panel/model/view'
|
||||
|
||||
// The features' actions reach the shared API barrel; the slot mapping calls none of it.
|
||||
vi.mock('@/shared/api', () => ({}))
|
||||
|
||||
const none: Record<ModuleName, boolean> = { kv: false, nats: false, qdrant: false, git: false, s3: false }
|
||||
const enable = (service: string) => <i data-enable={service} />
|
||||
|
||||
function slotFor(name: ModuleName, linked: Record<ModuleName, boolean>) {
|
||||
const slot = panelSlots({ linked, query: 'budget', gitHost: 'git.example.test', enable }).find((s) => s.name === name)
|
||||
if (!slot) throw new Error(`no slot for ${name}`)
|
||||
return slot.content
|
||||
}
|
||||
|
||||
describe('panelSlots', () => {
|
||||
it.each([
|
||||
['kv', 'redis'],
|
||||
['nats', 'nats'],
|
||||
['qdrant', 'qdrant'],
|
||||
['git', 'git'],
|
||||
['s3', 's3'],
|
||||
] as const)('renders the enable card for %s, naming %s, when its variables are absent', (name, service) => {
|
||||
const content = slotFor(name, none) as ReactElement<{ 'data-enable': string }>
|
||||
expect(isValidElement(content)).toBe(true)
|
||||
expect(content.props['data-enable']).toBe(service)
|
||||
})
|
||||
|
||||
it('renders the uploads counter for kv when linked', () => {
|
||||
expect((slotFor('kv', { ...none, kv: true }) as ReactElement).type).toBe(UploadsLeft)
|
||||
})
|
||||
|
||||
it('renders the similarity search for qdrant when linked, with the query', () => {
|
||||
const content = slotFor('qdrant', { ...none, qdrant: true }) as ReactElement<{ query: string }>
|
||||
expect(content.type).toBe(SearchNotes)
|
||||
expect(content.props.query).toBe('budget')
|
||||
})
|
||||
|
||||
it('renders the export button for s3 when linked', () => {
|
||||
expect((slotFor('s3', { ...none, s3: true }) as ReactElement).type).toBe(ExportNotesButton)
|
||||
})
|
||||
|
||||
it('names the git host the packages install from when git is linked', () => {
|
||||
const content = slotFor('git', { ...none, git: true }) as ReactElement<{ children: unknown[] }>
|
||||
expect(content.type).toBe('p')
|
||||
expect(content.props.children).toContain('git.example.test')
|
||||
})
|
||||
|
||||
it('renders no feature for a module whose variables are absent', () => {
|
||||
const content = slotFor('kv', none) as ReactElement
|
||||
expect(content.type).not.toBe(UploadsLeft)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { ExportNotesButton } from '@/features/export-notes'
|
||||
import { UploadsLeft } from '@/features/rate-limit'
|
||||
import { SearchNotes } from '@/features/search-notes'
|
||||
import { moduleView, type ModuleName } from '@/widgets/module-panel/model/view'
|
||||
|
||||
/** The service an optional module is linked through, as the platform's catalog names it. */
|
||||
export type ModuleService = 'redis' | 'nats' | 'qdrant' | 'git' | 's3'
|
||||
|
||||
export type ModuleSlot = { name: ModuleName; service: ModuleService; content: ReactNode }
|
||||
|
||||
/**
|
||||
* The panel's slots, one per optional module (D13): the module's feature when its
|
||||
* variables are present, the enable card the view supplies when they are not. A pure
|
||||
* function, so a test can assert what each slot renders without a server.
|
||||
*/
|
||||
export function panelSlots({
|
||||
linked,
|
||||
query,
|
||||
gitHost,
|
||||
enable,
|
||||
}: {
|
||||
linked: Record<ModuleName, boolean>
|
||||
query: string | undefined
|
||||
gitHost: string
|
||||
enable: (service: ModuleService) => ReactNode
|
||||
}): ModuleSlot[] {
|
||||
const features: Array<{ name: ModuleName; service: ModuleService; feature: ReactNode }> = [
|
||||
{ name: 'kv', service: 'redis', feature: <UploadsLeft /> },
|
||||
{ name: 'nats', service: 'nats', feature: <p>New notes are announced live on NATS.</p> },
|
||||
{ name: 'qdrant', service: 'qdrant', feature: <SearchNotes query={query} /> },
|
||||
{ name: 's3', service: 's3', feature: <ExportNotesButton /> },
|
||||
{ name: 'git', service: 'git', feature: <p>Packages install from {gitHost}.</p> },
|
||||
]
|
||||
return features.map((slot) => ({
|
||||
name: slot.name,
|
||||
service: slot.service,
|
||||
content: moduleView(slot.name, linked) === 'feature' ? slot.feature : enable(slot.service),
|
||||
}))
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { moduleView, type ModuleName } from '@/widgets/module-panel/model/view'
|
||||
|
||||
const names: ModuleName[] = ['kv', 'nats', 'qdrant', 'git', 's3']
|
||||
const none = { kv: false, nats: false, qdrant: false, git: false, s3: false }
|
||||
|
||||
describe('moduleView', () => {
|
||||
it.each(names)('shows the enable card for %s when its variables are absent', (name) => {
|
||||
expect(moduleView(name, none)).toBe('enable')
|
||||
})
|
||||
|
||||
it.each(names)('shows the feature for %s when its variables are present', (name) => {
|
||||
expect(moduleView(name, { ...none, [name]: true })).toBe('feature')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,9 @@
|
||||
/** The optional modules the panel shows (plan D13), by their env-derived names. */
|
||||
export type ModuleName = 'kv' | 'nats' | 'qdrant' | 'git' | 's3'
|
||||
|
||||
/** What a module's slot shows: its feature when linked, its enable card when not. */
|
||||
export type ModuleView = 'feature' | 'enable'
|
||||
|
||||
export function moduleView(name: ModuleName, linked: Record<ModuleName, boolean>): ModuleView {
|
||||
return linked[name] ? 'feature' : 'enable'
|
||||
}
|
||||
Reference in New Issue
Block a user