Per-file type-only fixes surfaced after the Supabase client was correctly typed. All fixes preserve runtime behavior; no any/ts-ignore/ts-expect-error. - lib/supabase/client.ts: annotate the browser client as SupabaseClient<Database> (same ssr/js drift as server.ts; fixes it centrally for all callers). - Supabase embedded-select joins whose FK metadata is missing from the generated types are bridged with `as unknown as <target>` + a TODO to remove after regenerating types/database.ts. - setState optimistic-update variance and raw <select> enum values cast to the precise element/union type; .insert payload fields (Json, enums) given the precise column type via satisfies/field casts. - Added narrowing guards where a value was legitimately nullable (comms channel, public form page) without changing the intended path. The 14 remaining errors are confined to the four files fixed in Phase B (real bugs + the tautological actions.test rewrite). 639 tests still green. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import type { ReactNode } from 'react'
|
|
import {
|
|
headingText,
|
|
slugifyHeading,
|
|
makeHeadingSlugger,
|
|
isInDocumentHref,
|
|
} from '@/lib/markdown-anchors'
|
|
|
|
describe('headingText', () => {
|
|
it('reads strings and numbers', () => {
|
|
expect(headingText('Hello')).toBe('Hello')
|
|
expect(headingText(42)).toBe('42')
|
|
})
|
|
it('flattens arrays and element children', () => {
|
|
expect(headingText(['Get ', 'started'])).toBe('Get started')
|
|
expect(headingText({ props: { children: ['Bold ', { props: { children: 'bit' } }] } } as ReactNode)).toBe('Bold bit')
|
|
})
|
|
it('ignores null/boolean', () => {
|
|
expect(headingText(null)).toBe('')
|
|
expect(headingText(true)).toBe('')
|
|
})
|
|
})
|
|
|
|
describe('slugifyHeading', () => {
|
|
it('lowercases, strips punctuation, hyphenates', () => {
|
|
expect(slugifyHeading('Overview')).toBe('overview')
|
|
expect(slugifyHeading(' Getting Started! ')).toBe('getting-started')
|
|
expect(slugifyHeading('A & B / C')).toBe('a-b-c')
|
|
})
|
|
it('collapses repeats and trims hyphens', () => {
|
|
expect(slugifyHeading('-- weird spacing --')).toBe('weird-spacing')
|
|
})
|
|
})
|
|
|
|
describe('makeHeadingSlugger', () => {
|
|
it('dedupes repeated headings within a document', () => {
|
|
const slug = makeHeadingSlugger()
|
|
expect(slug('Setup')).toBe('setup')
|
|
expect(slug('Setup')).toBe('setup-1')
|
|
expect(slug('Setup')).toBe('setup-2')
|
|
expect(slug('Other')).toBe('other')
|
|
})
|
|
it('falls back to "section" for empty headings', () => {
|
|
const slug = makeHeadingSlugger()
|
|
expect(slug('')).toBe('section')
|
|
expect(slug(' ')).toBe('section-1')
|
|
})
|
|
})
|
|
|
|
describe('isInDocumentHref', () => {
|
|
it('is true only for hash links', () => {
|
|
expect(isInDocumentHref('#overview')).toBe(true)
|
|
expect(isInDocumentHref('https://example.com')).toBe(false)
|
|
expect(isInDocumentHref('/ops')).toBe(false)
|
|
expect(isInDocumentHref(undefined)).toBe(false)
|
|
})
|
|
})
|