80 lines
3.6 KiB
TypeScript
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()
|
|
})
|
|
})
|