Skip to main content
Glama
test_vue_symbol_retrieval.py13.3 kB
import os import pytest from solidlsp import SolidLanguageServer from solidlsp.ls_config import Language from solidlsp.ls_types import SymbolKind pytestmark = pytest.mark.vue class TestVueSymbolRetrieval: @pytest.mark.parametrize("language_server", [Language.VUE], indirect=True) def test_request_containing_symbol_script_setup_function(self, language_server: SolidLanguageServer) -> None: file_path = os.path.join("src", "components", "CalculatorInput.vue") # First, get the document symbols to find the handleDigit function symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots() handle_digit_symbol = next((s for s in symbols[0] if s.get("name") == "handleDigit"), None) if not handle_digit_symbol or "range" not in handle_digit_symbol: pytest.skip("handleDigit symbol not found - test fixture may need updating") # Get a position inside the handleDigit function body # We'll use a line a few lines after the function start func_start_line = handle_digit_symbol["range"]["start"]["line"] position_inside_func = func_start_line + 1 position_character = 4 # Request the containing symbol for this position containing_symbol = language_server.request_containing_symbol( file_path, position_inside_func, position_character, include_body=True ) # Verify we found the correct containing symbol assert containing_symbol is not None, "Should find containing symbol inside handleDigit function" assert containing_symbol["name"] == "handleDigit", f"Expected handleDigit, got {containing_symbol.get('name')}" assert containing_symbol["kind"] in [ SymbolKind.Function, SymbolKind.Method, SymbolKind.Variable, ], f"Expected function-like kind, got {containing_symbol.get('kind')}" # Verify the body is included if available if "body" in containing_symbol: assert "handleDigit" in containing_symbol["body"], "Function body should contain function name" @pytest.mark.parametrize("language_server", [Language.VUE], indirect=True) def test_request_containing_symbol_computed_property(self, language_server: SolidLanguageServer) -> None: file_path = os.path.join("src", "components", "CalculatorInput.vue") # Find the formattedDisplay computed property symbols = language_server.request_document_symbols(file_path).get_all_symbols_and_roots() formatted_display_symbol = next((s for s in symbols[0] if s.get("name") == "formattedDisplay"), None) if not formatted_display_symbol or "range" not in formatted_display_symbol: pytest.skip("formattedDisplay computed property not found - test fixture may need updating") # Get a position inside the computed property body computed_start_line = formatted_display_symbol["range"]["start"]["line"] position_inside_computed = computed_start_line + 1 position_character = 4 # Request the containing symbol for this position containing_symbol = language_server.request_containing_symbol( file_path, position_inside_computed, position_character, include_body=True ) # Verify we found the correct containing symbol # The language server returns the arrow function inside computed() rather than # the variable name. This is technically correct from LSP's perspective. assert containing_symbol is not None, "Should find containing symbol inside computed property" assert containing_symbol["name"] in [ "formattedDisplay", "computed() callback", ], f"Expected formattedDisplay or computed() callback, got {containing_symbol.get('name')}" assert containing_symbol["kind"] in [ SymbolKind.Property, SymbolKind.Variable, SymbolKind.Function, ], f"Expected property/variable/function kind for computed, got {containing_symbol.get('kind')}" @pytest.mark.parametrize("language_server", [Language.VUE], indirect=True) def test_request_containing_symbol_no_containing_symbol(self, language_server: SolidLanguageServer) -> None: file_path = os.path.join("src", "components", "CalculatorInput.vue") # Position in the import statements at the top of the script setup # Line 1-6 contain imports in CalculatorInput.vue import_line = 2 import_character = 10 # Request containing symbol for a position in the imports containing_symbol = language_server.request_containing_symbol(file_path, import_line, import_character) # Should return None or empty dictionary for positions without containing symbol assert ( containing_symbol is None or containing_symbol == {} ), f"Expected None or empty dict for import position, got {containing_symbol}" @pytest.mark.parametrize("language_server", [Language.VUE], indirect=True) def test_request_referencing_symbols_store_function(self, language_server: SolidLanguageServer) -> None: store_file = os.path.join("src", "stores", "calculator.ts") # Find the 'add' action in the calculator store symbols = language_server.request_document_symbols(store_file).get_all_symbols_and_roots() add_symbol = next((s for s in symbols[0] if s.get("name") == "add"), None) if not add_symbol or "selectionRange" not in add_symbol: pytest.skip("add action not found in calculator store - test fixture may need updating") # Request referencing symbols for the add action (include_self=True to get at least the definition) sel_start = add_symbol["selectionRange"]["start"] ref_symbols = [ ref.symbol for ref in language_server.request_referencing_symbols(store_file, sel_start["line"], sel_start["character"], include_self=True) ] assert isinstance(ref_symbols, list), f"request_referencing_symbols should return a list, got {type(ref_symbols)}" for symbol in ref_symbols: assert "name" in symbol, "Referencing symbol should have a name" assert "kind" in symbol, "Referencing symbol should have a kind" vue_refs = [ symbol for symbol in ref_symbols if "location" in symbol and "uri" in symbol["location"] and ".vue" in symbol["location"]["uri"] ] if len(vue_refs) > 0: calculator_input_refs = [ ref for ref in vue_refs if "location" in ref and "uri" in ref["location"] and "CalculatorInput.vue" in ref["location"]["uri"] ] for ref in calculator_input_refs: assert "name" in ref, "Reference should have name" assert "location" in ref, "Reference should have location" @pytest.mark.parametrize("language_server", [Language.VUE], indirect=True) def test_request_referencing_symbols_composable(self, language_server: SolidLanguageServer) -> None: composable_file = os.path.join("src", "composables", "useFormatter.ts") # Find the useFormatter composable function symbols = language_server.request_document_symbols(composable_file).get_all_symbols_and_roots() use_formatter_symbol = next((s for s in symbols[0] if s.get("name") == "useFormatter"), None) if not use_formatter_symbol or "selectionRange" not in use_formatter_symbol: pytest.skip("useFormatter composable not found - test fixture may need updating") # Request referencing symbols for the composable sel_start = use_formatter_symbol["selectionRange"]["start"] ref_symbols = [ ref.symbol for ref in language_server.request_referencing_symbols(composable_file, sel_start["line"], sel_start["character"]) ] # Verify we found references - useFormatter is imported and used in CalculatorInput.vue assert ( len(ref_symbols) >= 1 ), f"useFormatter should have at least 1 reference (used in CalculatorInput.vue), found {len(ref_symbols)} references" # Check for references in Vue components vue_refs = [ symbol for symbol in ref_symbols if "location" in symbol and "uri" in symbol["location"] and ".vue" in symbol["location"]["uri"] ] # CalculatorInput.vue imports and uses useFormatter assert len(vue_refs) >= 1, f"Should find at least 1 Vue component reference to useFormatter, found {len(vue_refs)}" # Verify we found reference in CalculatorInput.vue specifically has_calculator_input_ref = any( "CalculatorInput.vue" in ref["location"]["uri"] for ref in vue_refs if "location" in ref and "uri" in ref["location"] ) assert has_calculator_input_ref, ( f"Should find reference to useFormatter in CalculatorInput.vue. " f"Found references in: {[ref['location']['uri'] for ref in vue_refs if 'location' in ref and 'uri' in ref['location']]}" ) @pytest.mark.parametrize("language_server", [Language.VUE], indirect=True) def test_vue_component_cross_references(self, language_server: SolidLanguageServer) -> None: input_file = os.path.join("src", "components", "CalculatorInput.vue") button_file = os.path.join("src", "components", "CalculatorButton.vue") definitions = language_server.request_definition(input_file, 4, 10) assert len(definitions) == 1, f"Should find exactly 1 definition for CalculatorButton import, got {len(definitions)}" assert ( "CalculatorButton.vue" in definitions[0]["relativePath"] ), f"Definition should point to CalculatorButton.vue, got {definitions[0]['relativePath']}" refs = language_server.request_references(input_file, 4, 10) assert len(refs) >= 2, ( f"Should find at least 2 references to CalculatorButton (import + template usages). " f"In CalculatorInput.vue, CalculatorButton is imported and used ~7 times in template. Found {len(refs)} references" ) button_symbols = language_server.request_document_symbols(button_file).get_all_symbols_and_roots() symbol_names = [s.get("name") for s in button_symbols[0]] assert "Props" in symbol_names, "CalculatorButton.vue should have Props interface" assert "handleClick" in symbol_names, "CalculatorButton.vue should have handleClick function" @pytest.mark.parametrize("language_server", [Language.VUE], indirect=True) def test_request_defining_symbol_import_resolution(self, language_server: SolidLanguageServer) -> None: file_path = os.path.join("src", "components", "CalculatorInput.vue") # Find the import position for useCalculatorStore # In CalculatorInput.vue (0-indexed lines): # Line 2: import { useCalculatorStore } from '@/stores/calculator' # Line 8: const store = useCalculatorStore() # We'll request definition at the position of "useCalculatorStore" in the usage line defining_symbol = language_server.request_defining_symbol(file_path, 8, 18) if defining_symbol is None: # Some language servers may not support go-to-definition at usage sites # Try at line 2 (import statement) instead defining_symbol = language_server.request_defining_symbol(file_path, 2, 18) # Verify we found a defining symbol assert defining_symbol is not None, "Should find defining symbol for useCalculatorStore" assert "name" in defining_symbol, "Defining symbol should have a name" assert defining_symbol.get("name") in [ "useCalculatorStore", "calculator", ], f"Expected useCalculatorStore or calculator, got {defining_symbol.get('name')}" # Verify it points to the store file if "location" in defining_symbol and "uri" in defining_symbol["location"]: assert ( "calculator.ts" in defining_symbol["location"]["uri"] ), f"Should point to calculator.ts, got {defining_symbol['location']['uri']}" @pytest.mark.parametrize("language_server", [Language.VUE], indirect=True) def test_request_defining_symbol_component_import(self, language_server: SolidLanguageServer) -> None: file_path = os.path.join("src", "components", "CalculatorInput.vue") definitions = language_server.request_definition(file_path, 4, 10) assert len(definitions) > 0, "Should find definition for CalculatorButton import" definition = definitions[0] assert definition["relativePath"] is not None, "Definition should have a relative path" assert ( "CalculatorButton.vue" in definition["relativePath"] ), f"Should point to CalculatorButton.vue, got {definition['relativePath']}" assert definition["range"]["start"]["line"] == 0, "Definition should point to start of .vue file" defining_symbol = language_server.request_defining_symbol(file_path, 4, 10) assert defining_symbol is None or "name" in defining_symbol, "If defining_symbol is found, it should have a name"

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/oraios/serena'

If you have feedback or need assistance with the MCP directory API, please join our Discord server