Files
helder/test/symbol-popup.test.tsx

80 lines
3.6 KiB
TypeScript

// @vitest-environment jsdom
//
// The ⌘-click popup: declarations, what they are built from, and the references.
import { afterEach, describe, expect, it, vi } from 'vitest'
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
import React from 'react'
import { SymbolPopup } from '../src/renderer/src/overlays'
import type { SymbolLookup } from '../src/renderer/src/types'
// jsdom has no layout, so the popup's "keep the highlighted row in view" effect
// needs a stub — the same one the other jsdom tests install.
if (!Element.prototype.scrollIntoView) Element.prototype.scrollIntoView = (): void => {}
afterEach(cleanup)
const noop = (): void => {}
function lookup(over: Partial<SymbolLookup> = {}): SymbolLookup {
return {
name: 'VaultSearch',
defs: [{
name: 'VaultSearch', path: 'app/Services/VaultSearch.php', line: 12, kind: 'class', ns: 'App\\Services',
parents: ['Base'], interfaces: ['Searchable', 'Arrayable'], traits: ['HasVault'],
}],
refs: [{ path: 'app/Mcp/Tools/Find.php', hits: [{ no: 8, ln: 'new VaultSearch($x)', ix: 4 }] }],
refCount: 1,
...over,
}
}
describe('SymbolPopup', () => {
it('lists the declaration, its relations and the references', () => {
render(<SymbolPopup name="VaultSearch" data={lookup()} onOpenAt={noop} onSymbol={noop} onClose={noop} />)
expect(screen.getByText('app/Services/VaultSearch.php')).toBeTruthy()
expect(screen.getByText('extends')).toBeTruthy()
expect(screen.getByText('Base')).toBeTruthy()
expect(screen.getByText('Searchable')).toBeTruthy()
expect(screen.getByText('Arrayable')).toBeTruthy()
expect(screen.getByText('HasVault')).toBeTruthy()
expect(screen.getByText('1 reference')).toBeTruthy()
expect(screen.getByText('app/Mcp/Tools/Find.php')).toBeTruthy()
})
it('opens the file at the clicked line', () => {
const onOpenAt = vi.fn()
render(<SymbolPopup name="VaultSearch" data={lookup()} onOpenAt={onOpenAt} onSymbol={noop} onClose={noop} />)
fireEvent.click(screen.getByText('app/Services/VaultSearch.php'))
expect(onOpenAt).toHaveBeenCalledWith('app/Services/VaultSearch.php', 12)
})
it('survives a declaration with no relation fields', () => {
// A def can arrive from a main process that predates those fields (a dev
// session that never restarted). It must render, not take the window down.
const bare = { name: 'A', path: 'app/A.php', line: 1, kind: 'class' } as never
const data = lookup({ defs: [bare], refs: [], refCount: 0 })
render(<SymbolPopup name="A" data={data} onOpenAt={noop} onSymbol={noop} onClose={noop} />)
expect(screen.getByText('app/A.php')).toBeTruthy()
expect(screen.queryByText('extends')).toBeNull()
})
it('names one declaration in full, and drops an empty Used in section', () => {
render(<SymbolPopup name="VaultSearch" data={lookup({ refs: [], refCount: 0 })} onOpenAt={noop} onSymbol={noop} onClose={noop} />)
expect(screen.getByText('App\\Services\\VaultSearch')).toBeTruthy()
expect(screen.queryByText('Used in')).toBeNull()
})
it('falls back to the short name when several classes share it', () => {
const two = lookup()
two.defs = [two.defs[0], { ...two.defs[0], path: 'app/Other/VaultSearch.php', ns: 'App\\Other' }]
render(<SymbolPopup name="VaultSearch" data={two} onOpenAt={noop} onSymbol={noop} onClose={noop} />)
expect(screen.getByText('VaultSearch')).toBeTruthy()
})
it('shows a searching state until the lookup lands', () => {
render(<SymbolPopup name="VaultSearch" data={null} onOpenAt={noop} onSymbol={noop} onClose={noop} />)
expect(screen.getByText('Searching…')).toBeTruthy()
})
})