1
- from typing import Any , Dict , Optional , Tuple
1
+ from typing import Any , Dict , Callable , List , Optional , Tuple
2
2
from unittest .mock import Mock
3
- from urllib .parse import unquote , urlparse
4
3
5
4
import pytest
5
+ from IPython .terminal .interactiveshell import TerminalInteractiveShell
6
6
from jedi import Project
7
- from lsprotocol .types import Position , TextDocumentIdentifier
7
+ from positron .positron_ipkernel import PositronIPyKernel
8
+ from pygls .workspace .text_document import TextDocument
9
+ from lsprotocol .types import (
10
+ CompletionParams ,
11
+ MarkupKind ,
12
+ Position ,
13
+ TextDocumentIdentifier ,
14
+ )
8
15
9
16
from positron .help import ShowTopicRequest
10
- from positron .positron_jedilsp import HelpTopicParams , positron_help_topic_request
17
+ from positron .positron_jedilsp import (
18
+ HelpTopicParams ,
19
+ positron_completion ,
20
+ positron_help_topic_request ,
21
+ )
22
+
11
23
24
+ @pytest .fixture
25
+ def mock_server (kernel : PositronIPyKernel ) -> Callable [[str , str ], Mock ]:
26
+ """
27
+ Minimum interface for a pylgs server to support LSP unit tests.
28
+ """
12
29
13
- def mock_server (uri : str , source : str , namespace : Dict [str , Any ]) -> Mock :
14
- document = Mock ()
15
- document .path = unquote (urlparse (uri ).path )
16
- document .source = source
30
+ # Return a function that returns a mock server rather than an instantiated mock server,
31
+ # since uri and source change between tests.
32
+ def inner (uri : str , source : str ) -> Mock :
33
+ server = Mock ()
34
+ server .client_capabilities .text_document .completion .completion_item .documentation_format = (
35
+ list (MarkupKind )
36
+ )
37
+ server .initialization_options .completion .disable_snippets = False
38
+ server .initialization_options .completion .resolve_eagerly = False
39
+ server .initialization_options .completion .ignore_patterns = []
40
+ server .kernel = kernel
41
+ server .project = Project ("" )
42
+ server .workspace .get_document .return_value = TextDocument (uri , source )
17
43
18
- server = Mock ()
19
- server .kernel .get_user_ns .return_value = namespace
20
- server .project = Project ("" )
21
- server .workspace .get_document .return_value = document
44
+ return server
22
45
23
- return server
46
+ return inner
24
47
25
48
26
49
@pytest .mark .parametrize (
27
- ("source" , "position" , "namespace" , "topic " ),
50
+ ("source" , "position" , "namespace" , "expected_topic " ),
28
51
[
29
52
# An unknown variable should not be resolved.
30
53
("x" , (0 , 0 ), {}, None ),
@@ -33,12 +56,53 @@ def mock_server(uri: str, source: str, namespace: Dict[str, Any]) -> Mock:
33
56
],
34
57
)
35
58
def test_positron_help_topic_request (
36
- source : str , position : Tuple [int , int ], namespace : Dict [str , Any ], topic : Optional [str ]
59
+ mock_server : Mock ,
60
+ shell : TerminalInteractiveShell ,
61
+ source : str ,
62
+ position : Tuple [int , int ],
63
+ namespace : Dict [str , Any ],
64
+ expected_topic : Optional [str ],
37
65
) -> None :
66
+ shell .user_ns .update (namespace )
67
+
38
68
params = HelpTopicParams (TextDocumentIdentifier ("file:///foo.py" ), Position (* position ))
39
- server = mock_server (params .text_document .uri , source , namespace )
40
- actual = positron_help_topic_request (server , params )
41
- if topic is None :
42
- assert actual is None
69
+ server = mock_server (params .text_document .uri , source )
70
+
71
+ topic = positron_help_topic_request (server , params )
72
+
73
+ if expected_topic is None :
74
+ assert topic is None
43
75
else :
44
- assert actual == ShowTopicRequest (topic )
76
+ assert topic == ShowTopicRequest (expected_topic )
77
+
78
+
79
+ @pytest .mark .parametrize (
80
+ ("source" , "position" , "namespace" , "expected_completions" ),
81
+ [
82
+ # When completions match a variable defined in the source _and_ a variable in the user's namespace,
83
+ # prefer the namespace variable.
84
+ ('x = {"a": 0}\n x["' , (1 , 3 ), {"x" : {"b" : 0 }}, ['"b"' ]),
85
+ ],
86
+ )
87
+ def test_positron_completion (
88
+ mock_server : Mock ,
89
+ shell : TerminalInteractiveShell ,
90
+ source : str ,
91
+ position : Tuple [int , int ],
92
+ namespace : Dict [str , Any ],
93
+ expected_completions : List [str ],
94
+ ) -> None :
95
+ shell .user_ns .update (namespace )
96
+
97
+ params = CompletionParams (TextDocumentIdentifier ("file:///foo.py" ), Position (* position ))
98
+ server = mock_server (params .text_document .uri , source )
99
+
100
+ completion_list = positron_completion (server , params )
101
+
102
+ assert completion_list is not None , "No completions returned"
103
+
104
+ # TODO: This is actually a bug, we shouldn't be returning magic completions when completing dict keys.
105
+ completions = [item for item in completion_list .items if not item .label .startswith ("%" )]
106
+
107
+ completion_labels = [item .label for item in completions ]
108
+ assert completion_labels == expected_completions , "Unexpected completion labels"
0 commit comments