@@ -2,6 +2,7 @@ describe('Controller: ParagraphCtrl', function() {
22 beforeEach ( angular . mock . module ( 'zeppelinWebApp' ) ) ;
33
44 let scope ;
5+ let rootScope ;
56 let websocketMsgSrvMock = { } ;
67 let paragraphMock = {
78 config : { } ,
@@ -19,6 +20,7 @@ describe('Controller: ParagraphCtrl', function() {
1920
2021 beforeEach ( inject ( function ( $controller , $rootScope ) {
2122 scope = $rootScope . $new ( ) ;
23+ rootScope = $rootScope ;
2224 $rootScope . notebookScope = $rootScope . $new ( true , $rootScope ) ;
2325
2426 $controller ( 'ParagraphCtrl' , {
@@ -50,4 +52,149 @@ describe('Controller: ParagraphCtrl', function() {
5052 it ( 'should set default value of "paragraphFocused" as false' , function ( ) {
5153 expect ( scope . paragraphFocused ) . toEqual ( false ) ;
5254 } ) ;
55+
56+ describe ( 'completion candidate filtering' , function ( ) {
57+ let FilteredList ;
58+ let originalSetFilter ;
59+
60+ let completionParagraph = {
61+ id : 'paragraph_completion' ,
62+ config : {
63+ editorSetting : {
64+ completionSupport : true ,
65+ } ,
66+ } ,
67+ settings : {
68+ forms : { } ,
69+ } ,
70+ } ;
71+
72+ let fromInterpreter = function ( value , meta ) {
73+ return { value : value , caption : value , meta : meta , score : 300 , fromBackend : true } ;
74+ } ;
75+
76+ let fromAce = function ( value , meta ) {
77+ return { value : value , caption : value , meta : meta , score : 0 } ;
78+ } ;
79+
80+ // the filter lives on ace's FilteredList prototype, installed on focus
81+ let applyFilter = function ( candidates ) {
82+ let list = new FilteredList ( candidates ) ;
83+ list . setFilter ( '' ) ;
84+ return list . filtered ;
85+ } ;
86+
87+ beforeEach ( function ( ) {
88+ FilteredList = ace . require ( 'ace/autocomplete' ) . FilteredList ;
89+ originalSetFilter = FilteredList . prototype . setFilter ;
90+ scope . init ( completionParagraph ) ;
91+ rootScope . $broadcast ( 'focusParagraph' , completionParagraph . id , 0 , 0 , true ) ;
92+ } ) ;
93+
94+ afterEach ( function ( ) {
95+ FilteredList . prototype . setFilter = originalSetFilter ;
96+ } ) ;
97+
98+ it ( 'should keep interpreter candidates that carry a meta label' , function ( ) {
99+ let table = fromInterpreter ( 'my_table' , 'table' ) ;
100+ expect ( applyFilter ( [ table ] ) ) . toContain ( table ) ;
101+ } ) ;
102+
103+ it ( 'should hide ace candidates while interpreter candidates are available' , function ( ) {
104+ let table = fromInterpreter ( 'my_table' , 'table' ) ;
105+ let local = fromAce ( 'my_local_var' , 'local' ) ;
106+ let keyword = fromAce ( 'select' , 'keyword' ) ;
107+
108+ let filtered = applyFilter ( [ table , local , keyword ] ) ;
109+
110+ expect ( filtered ) . toContain ( table ) ;
111+ expect ( filtered ) . not . toContain ( local ) ;
112+ expect ( filtered ) . not . toContain ( keyword ) ;
113+ } ) ;
114+
115+ describe ( 'when the interpreter answers with no candidates' , function ( ) {
116+ let editorElement ;
117+
118+ // completionListLength is only settable through a listener aceLoaded registers
119+ beforeEach ( function ( ) {
120+ websocketMsgSrvMock . getEditorSetting = function ( ) { } ;
121+ websocketMsgSrvMock . completion = function ( ) { } ;
122+
123+ editorElement = document . createElement ( 'div' ) ;
124+ editorElement . id = 'completion_test_editor' ;
125+ document . body . appendChild ( editorElement ) ;
126+
127+ scope . aceLoaded ( ace . edit ( editorElement ) ) ;
128+ rootScope . $broadcast ( 'completionListLength' , 0 ) ;
129+ } ) ;
130+
131+ afterEach ( function ( ) {
132+ document . body . removeChild ( editorElement ) ;
133+ } ) ;
134+
135+ it ( 'should fall back to ace candidates' , function ( ) {
136+ let local = fromAce ( 'my_local_var' , 'local' ) ;
137+ let keyword = fromAce ( 'select' , 'keyword' ) ;
138+
139+ let filtered = applyFilter ( [ local , keyword ] ) ;
140+
141+ expect ( filtered ) . toContain ( local ) ;
142+ expect ( filtered ) . toContain ( keyword ) ;
143+ } ) ;
144+ } ) ;
145+
146+ describe ( 'candidates built from an interpreter answer' , function ( ) {
147+ let editorElement ;
148+ let editor ;
149+
150+ beforeEach ( function ( ) {
151+ websocketMsgSrvMock . getEditorSetting = function ( ) { } ;
152+ websocketMsgSrvMock . completion = function ( ) { } ;
153+
154+ editorElement = document . createElement ( 'div' ) ;
155+ editorElement . id = 'completion_producer_editor' ;
156+ document . body . appendChild ( editorElement ) ;
157+
158+ editor = ace . edit ( editorElement ) ;
159+ scope . aceLoaded ( editor ) ;
160+ editor . focus ( ) ;
161+ } ) ;
162+
163+ afterEach ( function ( ) {
164+ document . body . removeChild ( editorElement ) ;
165+ } ) ;
166+
167+ // aceLoaded installs remoteCompleter as the first of ace's completers.
168+ let collectCandidates = function ( completions ) {
169+ let remoteCompleter = editor . completers [ 0 ] ;
170+ let received = null ;
171+
172+ remoteCompleter . getCompletions ( editor , editor . getSession ( ) , { row : 0 , column : 0 } , '' , function ( err , items ) {
173+ received = items ;
174+ } ) ;
175+ rootScope . $broadcast ( 'completionList' , { completions : completions } ) ;
176+
177+ return received ;
178+ } ;
179+
180+ it ( 'should mark every candidate as coming from the interpreter' , function ( ) {
181+ let candidates = collectCandidates ( [
182+ { name : 'my_table' , value : 'my_table' , meta : 'table' } ,
183+ { name : 'my_schema' , value : 'my_schema' , meta : 'schema' } ,
184+ ] ) ;
185+
186+ expect ( candidates ) . not . toBeNull ( ) ;
187+ expect ( candidates . length ) . toEqual ( 2 ) ;
188+ candidates . forEach ( function ( candidate ) {
189+ expect ( candidate . fromBackend ) . toBe ( true ) ;
190+ } ) ;
191+ } ) ;
192+
193+ it ( 'should produce candidates that survive the filter' , function ( ) {
194+ let candidates = collectCandidates ( [ { name : 'my_table' , value : 'my_table' , meta : 'table' } ] ) ;
195+
196+ expect ( applyFilter ( candidates ) ) . toEqual ( candidates ) ;
197+ } ) ;
198+ } ) ;
199+ } ) ;
53200} ) ;
0 commit comments