1+ // Copyright (c) Microsoft Corporation.
2+ // Licensed under the MIT License.
3+
4+ import { assert } from 'chai' ;
5+ import { OutputItem } from 'vscode-notebook-renderer' ;
6+
7+ /* eslint-disable @typescript-eslint/no-explicit-any */
8+
9+ // Mock the OutputItem interface for testing
10+ class MockOutputItem implements OutputItem {
11+ constructor (
12+ public id : string ,
13+ public mime : string ,
14+ private _data : any ,
15+ public metadata : Record < string , any > = { }
16+ ) { }
17+
18+ json ( ) : any {
19+ if ( this . mime . toLowerCase ( ) . includes ( 'json' ) ) {
20+ return this . _data ;
21+ }
22+ throw new Error ( 'Not JSON data' ) ;
23+ }
24+
25+ text ( ) : string {
26+ return typeof this . _data === 'string' ? this . _data : JSON . stringify ( this . _data ) ;
27+ }
28+
29+ blob ( ) : Blob {
30+ throw new Error ( 'Not implemented' ) ;
31+ }
32+
33+ data ( ) : Uint8Array {
34+ throw new Error ( 'Not implemented' ) ;
35+ }
36+ }
37+
38+ // Import the function we want to test
39+ import { convertVSCodeOutputToExecuteResultOrDisplayData } from './converter' ;
40+
41+ suite ( 'IPyWidget Renderer - convertVSCodeOutputToExecuteResultOrDisplayData' , ( ) => {
42+ test ( 'Should return widget data when JSON contains model_id' , ( ) => {
43+ const widgetData = {
44+ model_id : 'test-widget-123' ,
45+ version_major : 2 ,
46+ version_minor : 0 ,
47+ state : { value : 42 }
48+ } ;
49+
50+ const outputItem = new MockOutputItem ( 'test-1' , 'application/vnd.jupyter.widget-view+json' , widgetData ) ;
51+ const result = convertVSCodeOutputToExecuteResultOrDisplayData ( outputItem ) ;
52+
53+ assert . deepEqual ( result , widgetData ) ;
54+ } ) ;
55+
56+ test ( 'Should return undefined when JSON does not contain model_id' , ( ) => {
57+ const nonWidgetData = {
58+ content : '<div>Some HTML content</div>' ,
59+ metadata : { }
60+ } ;
61+
62+ const outputItem = new MockOutputItem ( 'test-2' , 'application/json' , nonWidgetData ) ;
63+ const result = convertVSCodeOutputToExecuteResultOrDisplayData ( outputItem ) ;
64+
65+ assert . isUndefined ( result ) ;
66+ } ) ;
67+
68+ test ( 'Should return widget data when text contains valid widget JSON' , ( ) => {
69+ const widgetData = {
70+ model_id : 'text-widget-456' ,
71+ version_major : 2 ,
72+ version_minor : 0 ,
73+ state : { text : 'Hello World' }
74+ } ;
75+
76+ const outputItem = new MockOutputItem ( 'test-3' , 'text/plain' , JSON . stringify ( widgetData ) ) ;
77+ const result = convertVSCodeOutputToExecuteResultOrDisplayData ( outputItem ) ;
78+
79+ assert . deepEqual ( result , widgetData ) ;
80+ } ) ;
81+
82+ test ( 'Should return undefined when text contains non-widget JSON' , ( ) => {
83+ const nonWidgetData = {
84+ content : 'Some content' ,
85+ type : 'text'
86+ } ;
87+
88+ const outputItem = new MockOutputItem ( 'test-4' , 'text/plain' , JSON . stringify ( nonWidgetData ) ) ;
89+ const result = convertVSCodeOutputToExecuteResultOrDisplayData ( outputItem ) ;
90+
91+ assert . isUndefined ( result ) ;
92+ } ) ;
93+
94+ test ( 'Should return undefined when text is not valid JSON' , ( ) => {
95+ const plainText = '<div>Some HTML content</div>' ;
96+
97+ const outputItem = new MockOutputItem ( 'test-5' , 'text/html' , plainText ) ;
98+ const result = convertVSCodeOutputToExecuteResultOrDisplayData ( outputItem ) ;
99+
100+ assert . isUndefined ( result ) ;
101+ } ) ;
102+
103+ test ( 'Should return undefined when text is regular plain text' , ( ) => {
104+ const plainText = 'This is just plain text content' ;
105+
106+ const outputItem = new MockOutputItem ( 'test-6' , 'text/plain' , plainText ) ;
107+ const result = convertVSCodeOutputToExecuteResultOrDisplayData ( outputItem ) ;
108+
109+ assert . isUndefined ( result ) ;
110+ } ) ;
111+
112+ test ( 'Should handle empty data gracefully' , ( ) => {
113+ const outputItem = new MockOutputItem ( 'test-7' , 'application/json' , null ) ;
114+ const result = convertVSCodeOutputToExecuteResultOrDisplayData ( outputItem ) ;
115+
116+ assert . isUndefined ( result ) ;
117+ } ) ;
118+
119+ test ( 'Should handle invalid JSON gracefully' , ( ) => {
120+ // Create a mock that throws an error on json() call
121+ const outputItem = new MockOutputItem ( 'test-8' , 'application/json' , 'invalid-json' ) ;
122+ // Override json method to throw
123+ outputItem . json = ( ) => {
124+ throw new Error ( 'Invalid JSON' ) ;
125+ } ;
126+
127+ const result = convertVSCodeOutputToExecuteResultOrDisplayData ( outputItem ) ;
128+
129+ assert . isUndefined ( result ) ;
130+ } ) ;
131+
132+ test ( 'Should return widget data for append_display_data widget output' , ( ) => {
133+ // This tests the specific case from the issue - append_display_data creates
134+ // outputs that should be processed as widget data when they contain model_id
135+ const appendDisplayData = {
136+ model_id : 'output-widget-789' ,
137+ version_major : 2 ,
138+ version_minor : 0 ,
139+ state : {
140+ outputs : [
141+ {
142+ output_type : 'display_data' ,
143+ data : {
144+ 'text/html' : '<div style="color: green;">Content added via append_display_data</div>'
145+ }
146+ }
147+ ]
148+ }
149+ } ;
150+
151+ const outputItem = new MockOutputItem ( 'test-9' , 'application/vnd.jupyter.widget-view+json' , appendDisplayData ) ;
152+ const result = convertVSCodeOutputToExecuteResultOrDisplayData ( outputItem ) ;
153+
154+ assert . deepEqual ( result , appendDisplayData ) ;
155+ assert . isNotNull ( result ) ;
156+ assert . equal ( ( result as any ) . model_id , 'output-widget-789' ) ;
157+ } ) ;
158+
159+ test ( 'Should return undefined for regular HTML content from append_display_data' , ( ) => {
160+ // This tests the case where append_display_data creates regular HTML/text content
161+ // that should NOT be processed by the widget renderer
162+ const regularContent = '<div style="color: blue;">Regular HTML content</div>' ;
163+
164+ const outputItem = new MockOutputItem ( 'test-10' , 'text/html' , regularContent ) ;
165+ const result = convertVSCodeOutputToExecuteResultOrDisplayData ( outputItem ) ;
166+
167+ assert . isUndefined ( result ) ;
168+ } ) ;
169+
170+ test ( 'Should handle widget data with complex nested structures' , ( ) => {
171+ const complexWidgetData = {
172+ model_id : 'complex-widget-999' ,
173+ version_major : 2 ,
174+ version_minor : 0 ,
175+ state : {
176+ children : [ 'IPY_MODEL_child1' , 'IPY_MODEL_child2' ] ,
177+ layout : {
178+ width : '100%' ,
179+ height : '200px'
180+ } ,
181+ style : {
182+ background_color : '#ffffff'
183+ }
184+ }
185+ } ;
186+
187+ const outputItem = new MockOutputItem ( 'test-11' , 'application/vnd.jupyter.widget-view+json' , complexWidgetData ) ;
188+ const result = convertVSCodeOutputToExecuteResultOrDisplayData ( outputItem ) ;
189+
190+ assert . deepEqual ( result , complexWidgetData ) ;
191+ assert . isNotNull ( result ) ;
192+ assert . equal ( ( result as any ) . model_id , 'complex-widget-999' ) ;
193+ assert . deepEqual ( ( result as any ) . state . children , [ 'IPY_MODEL_child1' , 'IPY_MODEL_child2' ] ) ;
194+ } ) ;
195+ } ) ;
0 commit comments