Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions plugins/interface/components/avatar/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { renderToString } from 'hono/jsx/dom/server'
import { describe, expect, it } from 'vitest'
import { Avatar } from './index'

describe('Avatar', () => {
it('renders a button avatar with the username initial', () => {
const html = renderToString(
<Avatar
class="custom-avatar"
size="sm"
toggled
username="starbase"
/>
)

expect(html).toContain('<button')
expect(html).toContain('custom-avatar')
expect(html).toContain('ob-size-sm')
expect(html).toContain('toggle')
expect(html).toContain('>S</p>')
})

it('renders an anchor avatar with an image when href and image are provided', () => {
const html = renderToString(
<Avatar
as="a"
href="/profile"
image="/avatar.png"
size="lg"
username="outerbase"
/>
)

expect(html).toContain('<a')
expect(html).toContain('href="/profile"')
expect(html).toContain('ob-size-lg')
expect(html).toContain('src="/avatar.png"')
expect(html).toContain('alt="outerbase"')
expect(html).toContain('height="36"')
expect(html).toContain('width="36"')
})
})
38 changes: 38 additions & 0 deletions plugins/interface/components/button/Button.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { renderToString } from 'hono/jsx/dom/server'
import { describe, expect, it } from 'vitest'
import { Button } from './Button'

describe('Button', () => {
it('renders a button with variant, size, title, and custom classes', () => {
const html = renderToString(
<Button
className="custom-class"
size="lg"
title="Save changes"
variant="primary"
>
<span>Icon</span>
</Button>
)

expect(html).toContain('<button')
expect(html).toContain('btn-primary')
expect(html).toContain('ob-size-lg')
expect(html).toContain('custom-class')
expect(html).toContain('Save changes')
expect(html).toContain('<span>Icon</span>')
})

it('uses anchor markup for href buttons and omits title text for square buttons', () => {
const html = renderToString(
<Button href="/docs" shape="square" title="Docs">
D
</Button>
)

expect(html).toContain('<a')
expect(html).toContain('href="/docs"')
expect(html).not.toContain('>Docs<')
expect(html).toContain('>D</a>')
})
})
29 changes: 29 additions & 0 deletions plugins/interface/components/card/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { renderToString } from 'hono/jsx/dom/server'
import { describe, expect, it } from 'vitest'
import { Card } from './index'

describe('Card', () => {
it('renders a secondary div card by default', () => {
const html = renderToString(
<Card className="custom-card">Card content</Card>
)

expect(html).toContain('<div')
expect(html).toContain('btn-secondary')
expect(html).toContain('custom-card')
expect(html).toContain('Card content')
})

it('renders an anchor card for link variants', () => {
const html = renderToString(
<Card as="a" href="/tables" variant="primary">
Tables
</Card>
)

expect(html).toContain('<a')
expect(html).toContain('href="/tables"')
expect(html).toContain('btn-primary')
expect(html).toContain('Tables')
})
})
43 changes: 43 additions & 0 deletions plugins/interface/components/input/Input.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { renderToString } from 'hono/jsx/dom/server'
import { describe, expect, it, vi } from 'vitest'
import { Input } from './Input'

describe('Input', () => {
it('renders a plain input with sizing, value, and invalid state classes', () => {
const html = renderToString(
<Input
className="custom-input"
initialValue="users"
isValid={false}
onValueChange={vi.fn()}
placeholder="Table"
size="lg"
/>
)

expect(html).toContain('<input')
expect(html).toContain('custom-input')
expect(html).toContain('ob-size-lg')
expect(html).toContain('text-ob-destructive')
expect(html).toContain('value="users"')
expect(html).toContain('placeholder="Table"')
})

it('wraps the input with preText and postText when provided', () => {
const html = renderToString(
<Input
initialValue="public"
onValueChange={vi.fn()}
postText=".sqlite"
preText="schema:"
size="sm"
/>
)

expect(html).toContain('<div')
expect(html).toContain('ob-size-sm')
expect(html).toContain('schema:')
expect(html).toContain('.sqlite')
expect(html).toContain('value="public"')
})
})
34 changes: 34 additions & 0 deletions plugins/interface/components/label/Label.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { renderToString } from 'hono/jsx/dom/server'
import { describe, expect, it } from 'vitest'
import { Label } from './Label'

describe('Label', () => {
it('renders title and children', () => {
const html = renderToString(
<Label className="custom-label" title="Database">
<input name="database" />
</Label>
)

expect(html).toContain('<label')
expect(html).toContain('custom-label')
expect(html).toContain('Database')
expect(html).toContain('name="database"')
})

it('renders required validation text when invalid', () => {
const html = renderToString(
<Label
isValid={false}
required
requiredDescription="required"
title="Name"
/>
)

expect(html).toContain('Name')
expect(html).toContain('*')
expect(html).toContain('required')
expect(html).toContain('text-ob-destructive')
})
})
15 changes: 15 additions & 0 deletions plugins/interface/components/loader/Loader.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { renderToString } from 'hono/jsx/dom/server'
import { describe, expect, it } from 'vitest'
import { Loader } from './Loader'

describe('Loader', () => {
it('renders an animated svg loader with custom size and class', () => {
const html = renderToString(<Loader class="custom-loader" size={16} />)

expect(html).toContain('<svg')
expect(html).toContain('custom-loader')
expect(html).toContain('height: 16px; width: 16px')
expect(html).toContain('animateTransform')
expect(html).toContain('repeatCount="indefinite"')
})
})
27 changes: 27 additions & 0 deletions plugins/interface/components/select/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { renderToString } from 'hono/jsx/dom/server'
import { describe, expect, it, vi } from 'vitest'
import { Select } from './index'

describe('Select', () => {
it('renders placeholder and options with the selected value', () => {
const html = renderToString(
<Select
className="custom-select"
options={['alpha', 'beta']}
placeholder="Choose"
setValue={vi.fn()}
size="sm"
value="beta"
/>
)

expect(html).toContain('<select')
expect(html).toContain('custom-select')
expect(html).toContain('ob-size-sm')
expect(html).toContain('value="beta"')
expect(html).toContain('<option>Choose</option>')
expect(html).toContain('<option value="alpha">alpha</option>')
expect(html).toContain('<option value="beta">beta</option>')
expect(html).toContain('background-image:url(/caret.svg)')
})
})
27 changes: 27 additions & 0 deletions plugins/interface/components/toggle/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { renderToString } from 'hono/jsx/dom/server'
import { describe, expect, it, vi } from 'vitest'
import { Toggle } from './index'

describe('Toggle', () => {
it('renders untoggled state with base sizing', () => {
const html = renderToString(
<Toggle onClick={vi.fn()} toggled={false} />
)

expect(html).toContain('<button')
expect(html).toContain('h-6.5')
expect(html).toContain('w-10.5')
expect(html).not.toContain('translate-x-full')
})

it('renders toggled state with large sizing', () => {
const html = renderToString(
<Toggle onClick={vi.fn()} size="lg" toggled />
)

expect(html).toContain('h-7.5')
expect(html).toContain('w-12.5')
expect(html).toContain('translate-x-full')
expect(html).toContain('bg-neutral-900')
})
})
32 changes: 32 additions & 0 deletions plugins/interface/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, expect, it } from 'vitest'
import { Hono } from 'hono'
import { InterfacePlugin } from './index'

describe('InterfacePlugin', () => {
it('registers the template route and exposes supported route matching', async () => {
const app = new Hono()
const plugin = new InterfacePlugin()
const dataSource = { rpc: { executeQuery: async () => [] } }

app.use('*', async (c, next) => {
c.set('dataSource', dataSource)
await next()
})

await plugin.register(app as any)

expect(plugin.supportedRoutes).toEqual(['/template'])
expect(plugin.matchesRoute('/template')).toBe(true)
expect(plugin.matchesRoute('/template/extra')).toBe(false)
expect(plugin.matchesRoute('/other')).toBe(false)

const response = await app.request('http://localhost/template')
const html = await response.text()

expect(response.status).toBe(200)
expect(plugin.dataSource).toBe(dataSource)
expect(html).toContain('<title>StarbaseDB</title>')
expect(html).toContain('id="root"')
expect(html).toContain('data-client="template"')
})
})
42 changes: 42 additions & 0 deletions plugins/interface/utils/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { describe, expect, it } from 'vitest'
import { cn, getAssetImportTagsFromManifest } from './index'

describe('cn', () => {
it('combines conditional classes and resolves Tailwind conflicts', () => {
const result = cn(
'flex px-2 text-sm',
false && 'hidden',
['items-center'],
{ 'text-ob-base-300': true, hidden: false },
'px-4'
)

expect(result).toContain('flex')
expect(result).toContain('items-center')
expect(result).toContain('text-sm')
expect(result).toContain('text-ob-base-300')
expect(result).toContain('px-4')
expect(result).not.toContain('hidden')
expect(result).not.toContain('px-2')
})
})

describe('getAssetImportTagsFromManifest', () => {
it('returns script and stylesheet tags for the current page and shared chunks', async () => {
const tags = (await getAssetImportTagsFromManifest('template')) as any[]

expect(tags).toHaveLength(4)
expect(tags.map((tag) => tag.props.src ?? tag.props.href)).toEqual([
'/assets/components.Dd6m7Hsm.js',
'/assets/vendor.DLA8GOwG.js',
'/assets/template.Cbhlkt6E.js',
'/assets/template.BByNnpth.css',
])
expect(tags.map((tag) => tag.props.type ?? tag.props.rel)).toEqual([
'module',
'module',
'module',
'stylesheet',
])
})
})