// @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 { 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() 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() 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() 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() 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() expect(screen.getByText('VaultSearch')).toBeTruthy() }) it('shows a searching state until the lookup lands', () => { render() expect(screen.getByText('Searching…')).toBeTruthy() }) })