1- import * as React from 'react' ;
2-
3- import { render } from '@testing-library/react' ;
41import type * as MonacoType from 'monaco-editor' ;
5- import { MosaicContext } from 'react-mosaic-component' ;
2+ import type { MosaicContext } from 'react-mosaic-component' ;
63import { beforeEach , describe , expect , it , vi } from 'vitest' ;
74
5+ import { renderClassComponentWithInstanceRef } from '../../../rtl-spec/test-utils/renderClassComponentWithInstanceRef' ;
86import { Output } from '../../../src/renderer/components/output' ;
7+ import { WrapperEditorId } from '../../../src/renderer/components/output-editors-wrapper' ;
98import { AppState } from '../../../src/renderer/state' ;
109import { MonacoMock } from '../../mocks/mocks' ;
1110
12- const mockMosaicActions = {
13- expand : vi . fn ( ) ,
14- remove : vi . fn ( ) ,
15- hide : vi . fn ( ) ,
16- replaceWith : vi . fn ( ) ,
17- updateTree : vi . fn ( ) ,
18- getRoot : vi . fn ( ) ,
19- } ;
20-
21- const mockContextValue = {
22- mosaicActions : mockMosaicActions ,
23- mosaicId : 'output' ,
24- } as any ;
25-
26- function renderOutput ( store : AppState , monaco : typeof MonacoType ) {
27- const ref = React . createRef < any > ( ) ;
28- const renderResult = render (
29- < MosaicContext . Provider value = { mockContextValue } >
30- < Output appState = { store } monaco = { monaco } monacoOptions = { { } } ref = { ref } />
31- </ MosaicContext . Provider > ,
11+ const mockContext = vi . hoisted (
12+ ( ) =>
13+ ( {
14+ mosaicActions : {
15+ expand : vi . fn ( ) ,
16+ remove : vi . fn ( ) ,
17+ hide : vi . fn ( ) ,
18+ replaceWith : vi . fn ( ) ,
19+ updateTree : vi . fn ( ) ,
20+ getRoot : vi . fn ( ) ,
21+ } ,
22+ mosaicId : 'output' ,
23+ } ) as unknown as MosaicContext < WrapperEditorId > ,
24+ ) ;
25+
26+ // Provide a default value for MosaicContext so that the component's
27+ // `static contextType = MosaicContext` picks up `mockContext` without
28+ // needing a Provider in the render tree.
29+ vi . mock ( 'react-mosaic-component' , async ( ) => {
30+ const React = await import ( 'react' ) ;
31+ const actual = await vi . importActual < typeof import ( 'react-mosaic-component' ) > (
32+ 'react-mosaic-component' ,
3233 ) ;
33- return { instance : ref . current ! , renderResult } ;
34- }
34+
35+ return {
36+ ...actual ,
37+ MosaicContext : React . createContext ( mockContext ) ,
38+ } ;
39+ } ) ;
3540
3641describe ( 'Output component' , ( ) => {
3742 let store : AppState ;
@@ -40,26 +45,40 @@ describe('Output component', () => {
4045 beforeEach ( ( ) => {
4146 monaco = window . monaco ;
4247 ( { state : store } = window . app ) ;
43- vi . clearAllMocks ( ) ;
48+ vi . mocked ( mockContext . mosaicActions . replaceWith ) . mockClear ( ) ;
49+ vi . mocked ( mockContext . mosaicActions . getRoot ) . mockReset ( ) ;
4450 } ) ;
4551
4652 it ( 'renders the output container' , ( ) => {
47- const { renderResult } = renderOutput ( store , monaco ) ;
53+ const { renderResult } = renderClassComponentWithInstanceRef ( Output , {
54+ appState : store ,
55+ monaco,
56+ monacoOptions : { } ,
57+ } ) ;
4858 const outputDiv = renderResult . container . querySelector ( '.output' ) ;
4959 expect ( outputDiv ) . toBeInTheDocument ( ) ;
5060 expect ( outputDiv ) . toHaveStyle ( 'display: inline-block' ) ;
5161 } ) ;
5262
5363 it ( 'correctly sets the language' , ( ) => {
54- const { instance } = renderOutput ( store , monaco ) ;
64+ const { instance } = renderClassComponentWithInstanceRef ( Output , {
65+ appState : store ,
66+ monaco,
67+ monacoOptions : { } ,
68+ } ) ;
5569 expect ( instance . language ) . toBe ( 'consoleOutputLanguage' ) ;
5670 } ) ;
5771
5872 describe ( 'initMonaco()' , ( ) => {
5973 it ( 'attempts to create an editor' , async ( ) => {
60- const { instance } = renderOutput ( store , monaco ) ;
61-
62- instance . outputRef . current = 'ref' as any ;
74+ const { instance } = renderClassComponentWithInstanceRef ( Output , {
75+ appState : store ,
76+ monaco,
77+ monacoOptions : { } ,
78+ } ) ;
79+
80+ // outputRef is private — cast needed to test initMonaco directly
81+ ( instance as any ) . outputRef . current = 'ref' ;
6382 await instance . initMonaco ( ) ;
6483
6584 expect ( monaco . editor . create ) . toHaveBeenCalled ( ) ;
@@ -68,9 +87,14 @@ describe('Output component', () => {
6887 } ) ;
6988
7089 it ( 'componentWillUnmount() attempts to dispose the editor' , async ( ) => {
71- const { instance } = renderOutput ( store , monaco ) ;
90+ const { instance } = renderClassComponentWithInstanceRef ( Output , {
91+ appState : store ,
92+ monaco,
93+ monacoOptions : { } ,
94+ } ) ;
7295
73- instance . outputRef . current = 'ref' as any ;
96+ // outputRef is private
97+ ( instance as any ) . outputRef . current = 'ref' ;
7498 await instance . initMonaco ( ) ;
7599 instance . componentWillUnmount ( ) ;
76100
@@ -80,22 +104,25 @@ describe('Output component', () => {
80104 } ) ;
81105
82106 it ( 'hides the console with react-mosaic-component' , async ( ) => {
83- const { instance } = renderOutput ( store , monaco ) ;
84-
85107 // direction is required to be recognized as a valid root node
86- mockMosaicActions . getRoot . mockReturnValue ( {
108+ vi . mocked ( mockContext . mosaicActions . getRoot ) . mockReturnValue ( {
87109 splitPercentage : 25 ,
88110 direction : 'row' ,
111+ } as ReturnType < typeof mockContext . mosaicActions . getRoot > ) ;
112+
113+ const { instance } = renderClassComponentWithInstanceRef ( Output , {
114+ appState : store ,
115+ monaco,
116+ monacoOptions : { } ,
89117 } ) ;
90118
91- instance . outputRef . current = 'ref' as any ;
119+ // outputRef is private
120+ ( instance as any ) . outputRef . current = 'ref' ;
92121 await instance . initMonaco ( ) ;
93-
94- // Trigger toggleConsole explicitly
95122 instance . toggleConsole ( ) ;
96123
97- expect ( mockMosaicActions . replaceWith ) . toHaveBeenCalled ( ) ;
98- expect ( mockMosaicActions . replaceWith ) . toHaveBeenCalledWith (
124+ expect ( mockContext . mosaicActions . replaceWith ) . toHaveBeenCalled ( ) ;
125+ expect ( mockContext . mosaicActions . replaceWith ) . toHaveBeenCalledWith (
99126 [ ] ,
100127 expect . objectContaining ( { splitPercentage : 25 } ) ,
101128 ) ;
@@ -114,12 +141,16 @@ describe('Output component', () => {
114141 } ,
115142 ] ;
116143
117- const { instance } = renderOutput ( store , monaco ) ;
144+ const { instance } = renderClassComponentWithInstanceRef ( Output , {
145+ appState : store ,
146+ monaco,
147+ monacoOptions : { } ,
148+ } ) ;
118149
119- instance . outputRef . current = 'ref' as any ;
150+ // outputRef and updateModel are private
151+ ( instance as any ) . outputRef . current = 'ref' ;
120152 await instance . initMonaco ( ) ;
121- // updateModel is private — cast needed to test it directly
122- ( instance as any ) . updateModel ( ) ;
153+ await ( instance as any ) . updateModel ( ) ;
123154
124155 expect ( monaco . editor . createModel ) . toHaveBeenCalled ( ) ;
125156 expect ( instance . editor ?. revealLine ) . toHaveBeenCalled ( ) ;
@@ -133,15 +164,19 @@ describe('Output component', () => {
133164 } ,
134165 ] ;
135166
136- const { instance } = renderOutput ( store , monaco ) ;
167+ const { instance } = renderClassComponentWithInstanceRef ( Output , {
168+ appState : store ,
169+ monaco,
170+ monacoOptions : { } ,
171+ } ) ;
137172 // updateModel is private — cast needed to spy on it
138173 const spy = vi . spyOn ( instance as any , 'updateModel' ) ;
139174
140- instance . outputRef . current = 'ref' as any ;
175+ // outputRef is private
176+ ( instance as any ) . outputRef . current = 'ref' ;
141177 await instance . initMonaco ( ) ;
142178
143- // updateModel is private
144- ( instance as any ) . updateModel ( ) ;
179+ await ( instance as any ) . updateModel ( ) ;
145180
146181 // new output
147182 store . output = [
@@ -159,13 +194,17 @@ describe('Output component', () => {
159194 } ) ;
160195
161196 it ( 'handles componentDidUpdate' , async ( ) => {
162- const { instance } = renderOutput ( store , monaco ) ;
197+ const { instance } = renderClassComponentWithInstanceRef ( Output , {
198+ appState : store ,
199+ monaco,
200+ monacoOptions : { } ,
201+ } ) ;
163202 const spy = vi . spyOn ( instance , 'toggleConsole' ) ;
164203
165- instance . outputRef . current = 'ref' as any ;
204+ // outputRef and updateModel are private
205+ ( instance as any ) . outputRef . current = 'ref' ;
166206 await instance . initMonaco ( ) ;
167207
168- // updateModel is private
169208 await ( instance as any ) . updateModel ( ) ;
170209 expect ( spy ) . toHaveBeenCalled ( ) ;
171210 } ) ;
0 commit comments